2013-09-01 66 views
6

Tôi có thể đang cố gắng đạt được điều không thể, nhưng StackExchange luôn làm tôi ngạc nhiên, vì vậy hãy đi theo hướng này:Bản đồ có thể được kiểm tra theo thời gian biên dịch có thể không?

Tôi cần ánh xạ tên thành số nguyên. Tên (khoảng 2k) là duy nhất. Sẽ không có thêm hoặc xóa bỏ danh sách đó và các giá trị sẽ không thay đổi trong thời gian chạy.

Triển khai chúng dưới dạng const int biến cho phép tôi kiểm tra thời gian biên dịch cho sự tồn tại và loại. Ngoài ra điều này rất rõ ràng và tiết trong mã. Lỗi dễ bị phát hiện.

Triển khai chúng dưới dạng std::map<std::string, int> mang lại cho tôi nhiều tính linh hoạt để xây dựng tên để tìm kiếm bằng thao tác chuỗi. Tôi có thể sử dụng điều này để cung cấp cho các chuỗi như các tham số cho các hàm có thể truy vấn danh sách cho nhiều giá trị bằng cách chắp thêm tiền tố/hậu tố vào chuỗi đó. Tôi cũng có thể lặp qua nhiều giá trị bằng cách tạo một phần số của tên khóa từ biến vòng lặp.

Bây giờ câu hỏi của tôi là: có phương pháp kết hợp cả hai lợi thế không? Việc thiếu kiểm tra thời gian biên dịch (đặc biệt là đối với khóa-tồn tại) gần như giết chết phương pháp thứ hai cho tôi. (Đặc biệt là std::map âm thầm trả về 0 nếu khóa không tồn tại gây khó tìm lỗi.) Nhưng khả năng lặp và tiền tố/hậu tố bổ sung rất hữu ích.

Tôi thích một giải pháp không sử dụng bất kỳ thư viện bổ sung nào như tăng cường, nhưng vui lòng đề xuất chúng dù sao tôi vẫn có thể triển khai lại chúng.

Một ví dụ về những gì tôi làm với bản đồ:

void init(std::map<std::string, int> &labels) 
{   
    labels.insert(std::make_pair("Bob1" , 45)); 
    labels.insert(std::make_pair("Bob2" , 8758)); 
    labels.insert(std::make_pair("Bob3" , 436)); 
    labels.insert(std::make_pair("Alice_first" , 9224)); 
    labels.insert(std::make_pair("Alice_last" , 3510)); 
} 

int main() 
{  
    std::map<std::string, int> labels; 
    init(labels); 

    for (int i=1; i<=3; i++) 
    { 
    std::stringstream key; 
    key << "Bob" << i; 
    doSomething(labels[key.str()]); 
    } 

    checkName("Alice"); 
} 

void checkName(std::string name) 
{ 
    std::stringstream key1,key2; 
    key1 << name << "_first"; 
    key2 << name << "_last"; 
    doFirstToLast(labels[key1.str()], labels[key2.str()]); 
} 

mục tiêu khác là mã hiển thị trong main() ở lại thói quen dễ dàng và tiết càng tốt. (Cần phải được hiểu bởi những người không lập trình.) Hàm init() sẽ được tạo mã bởi một số công cụ. Các hàm doSomething(int) được cố định, nhưng tôi có thể viết các hàm bao quanh chúng. Những người trợ giúp như checkName() có thể phức tạp hơn, nhưng cần phải dễ dàng gỡ lỗi.

+1

Có vẻ như bạn muốn xây dựng các dây lúc chạy, nhưng bằng cách nào đó đã này kiểm tra tại thời gian biên dịch? –

+0

Có nhiều cách để chuyển đổi enums thành các chuỗi thích hợp của chúng, vì vậy nó sẽ có thể .. Mặc dù khó có thể xem được thời gian biên dịch như thế nào nếu bạn xây dựng các chuỗi trong thời gian chạy –

+0

'std :: map' chắc chắn có khả năng cho dù một phần tử đã được chèn vào chưa. Một cách là 'chèn'. – chris

Trả lời

1

Một cách để thực hiện ví dụ bạn đang sử dụng một enum và dán mã thông báo, như thế này

enum { 
    Bob1 = 45, 
    Bob2 = 8758, 
    Bob3 = 436, 
    Alice_first = 9224, 
    Alice_last = 3510 
}; 

#define LABEL(a, b) (a ## b) 

int main() 
{  

    doSomething(LABEL(Bob,1)); 
    doSomething(LABEL(Bob,2)); 
    doSomething(LABEL(Bob,3)); 
} 


void checkName() 
{ 
    doFirstToLast(LABEL(Alice,_first), LABEL(Alice,_last)); 
} 

hay không đây là tốt nhất phụ thuộc vào nơi tên đến từ.

Nếu bạn cần hỗ trợ để sử dụng hợp vòng lặp, sau đó xem xét

int bob[] = { 0, Bob1, Bob2, Bob3 }; // Values from the enum 

int main() 
{  
    for(int i = 1; i <= 3; i++) { 
    doSomething(bob[i]); 
    } 
} 
+0

Mặc dù sử dụng bộ tiền xử lý (đi kèm với các vấn đề của chính nó trong quá trình gỡ lỗi), điều này giải quyết vấn đề khá độc đáo và tạo ra mã khá dễ hiểu và dễ hiểu. Tôi sẽ cần phải kiểm tra trong mã thực nếu nó thực sự đánh dấu tất cả các hộp, nhưng tôi đã thích nó. –

+0

Mặc dù không thể thực hiện các vòng lặp, vì điều này sẽ vẫn chỉ có sẵn khi chạy. Hoặc là có một thủ thuật không ác để làm cho vòng lặp với bộ tiền xử lý? –

1

Tôi không chắc mình hiểu tất cả các yêu cầu của bạn, nhưng làm thế nào về điều này, mà không cần sử dụng std::map. Tôi giả định rằng bạn có ba chuỗi, "FIRST", "SECOND" và "THIRD" mà bạn muốn ánh xạ tới 42, 17 và 37, tương ứng.

#include <stdio.h> 

const int m_FIRST = 0; 
const int m_SECOND = 1; 
const int m_THIRD = 2; 

const int map[] = {42, 17, 37}; 

#define LOOKUP(s) (map[m_ ## s]) 

int main() 
{ 
    printf("%d\n", LOOKUP(FIRST)); 
    printf("%d\n", LOOKUP(SECOND)); 
    return 0; 
} 

Bất lợi là bạn không thể sử dụng chuỗi biến với LOOKUP. Nhưng bây giờ bạn có thể lặp qua các giá trị.

+0

Ý tưởng thú vị. Giới hạn vòng lặp cho các giá trị liên tiếp, nhưng điều đó thực sự khá chấp nhận được. Thật không may, nó cũng khó để gỡ lỗi (cố gắng tìm giá trị 1276 trong mảng). –

0

Sử dụng enum bạn đã kiểm tra cả thời gian biên dịch và bạn có thể lặp qua nó:

How can I iterate over an enum?

+0

Liên kết nói rằng tôi không thể lặp lại nó nếu tôi sử dụng các số tùy ý cho ENUM, trường hợp này nếu tôi sử dụng enum để lưu trữ một bản đồ tên-to-int. –

1

Có lẽ một cái gì đó như thế này (chưa được kiểm tra)?

struct Bob { 
    static constexpr int values[3] = { 45, 8758, 436 }; 
}; 

struct Alice { 
    struct first { 
     static const int value = 9224; 
    }; 
    struct last { 
     static const int value = 3510; 
    }; 
}; 

template <typename NAME> 
void checkName() 
{ 
    doFirstToLast(NAME::first::value, NAME::last::value); 
} 

...

constexpr int Bob::values[3]; // need a definition in exactly one TU 

int main() 
{ 
    for (int i=1; i<=3; i++) 
    { 
     doSomething(Bob::values[i]); 
    } 

    checkName<Alice>(); 
} 
+0

Ý tưởng hay, nhưng không thể trong trường hợp của tôi. Sự phức tạp của bộ tạo mã điền vào danh sách sẽ phải được tăng lên rất nhiều. Tôi có thể sửa đổi cách xuất từng dòng, nhưng tôi không thể thêm quyết định phức tạp. –