2009-05-13 34 views
30

Gần đây tôi đã làm việc trên một số thiết bị nhúng, nơi chúng tôi có một số cấu trúc và công đoàn cần được khởi tạo vào thời gian biên dịch để chúng tôi có thể giữ những thứ nhất định trong flash hoặc ROM không cần sửa đổi và lưu một chút flash hoặc SRAM với một chút chi phí hiệu suất. Hiện tại, mã biên dịch là C99 hợp lệ, nhưng không có sự điều chỉnh này, nó được sử dụng để biên dịch thành mã C++, và nó sẽ rất tuyệt vời để hỗ trợ mọi thứ được biên dịch theo cách đó. Một trong những điều quan trọng ngăn cản điều này là chúng tôi đang sử dụng C99 initializers được chỉ định mà không làm việc trong C tập hợp con của C + +. Tôi không biết nhiều về C++ buff, vì vậy tôi tự hỏi những cách đơn giản nào có thể xảy ra trong C++ tương thích C, hoặc trong C++ vẫn cho phép khởi tạo tại thời gian biên dịch sao cho các cấu trúc và công đoàn không cần được khởi tạo sau khi khởi động chương trình trong SRAM.C++ Tương đương với Trình khởi tạo được Chỉ định?

Một điểm bổ sung cần lưu ý: lý do chính cho việc sử dụng trình khởi tạo được chỉ định là sinh lời không phải là thành viên đầu tiên của công đoàn. Ngoài ra, gắn bó với chuẩn C++ hoặc ANSI C là một điểm cộng để duy trì khả năng tương thích với các trình biên dịch khác (tôi biết về các phần mở rộng GNU cung cấp một cái gì đó giống như các trình khởi tạo được chỉ định không có C99).

+1

Lưu ý rằng initializers định bây giờ làm việc trong g ++. Tôi có phiên bản 4.8.1 và tôi có thể sử dụng initializers từ một enum và nó làm việc như mong đợi. –

Trả lời

19

Tôi không chắc chắn bạn có thể làm điều đó trong C++. Đối với những thứ mà bạn cần phải khởi tạo sử dụng initializers được chỉ định, bạn có thể đặt những cách riêng biệt trong một tập tin .c biên soạn như C99, ví dụ:

// In common header file 
typedef union my_union 
{ 
    int i; 
    float f; 
} my_union; 

extern const my_union g_var; 

// In file compiled as C99 
const my_union g_var = { .f = 3.14159f }; 

// Now any file that #include's the header can access g_var, and it will be 
// properly initialized at load time 
+3

+1 Hammers cho móng tay, tua vít dùng cho ốc vít. –

+2

Tôi đã xem xét điều đó, và cuối cùng có thể đi theo con đường đó. Thật không may có một nền tảng mà chúng tôi muốn chuyển sang sử dụng thư viện C++ để cung cấp hỗ trợ perhipheral. Họ cung cấp truy cập vào một trình biên dịch trực tuyến, nhưng họ đã giới hạn nó vào một chế độ C++ - chỉ (để đơn giản). Tôi đã thử grabbing thư viện biên dịch trước và sử dụng một toolchain GCC địa phương, nhưng có một số biểu tượng không được giải quyết từ thư viện của họ (Họ sử dụng RealView Keil/ARM) khi liên kết với GCC & Newlib. Tôi có thể biên dịch trước tất cả mã C99 tại địa phương và liên kết trực tuyến. Tôi chỉ cố gắng để giữ cho mọi thứ đơn giản :-) –

+7

Điều này không giải quyết được vấn đề trong C++. –

1

Các mã sau biên dịch không có vấn đề với g ++:

#include <iostream> 

struct foo 
{ 
    int a; 
    int b; 
    int c; 
}; 

union bar 
{ 
    int a; 
    float b; 
    long c; 
}; 

static foo s_foo1 = {1,2,3}; 
static foo s_foo2 = {1,2}; 
static bar s_bar1 = {42L}; 
static bar s_bar2 = {1078523331}; // 3.14 in float 


int main(int, char**) 
{ 
    std::cout << s_foo1.a << ", " << 
       s_foo1.b << ", " << 
       s_foo1.c << std::endl; 

    std::cout << s_foo2.a << ", " << 
       s_foo2.b << ", " << 
       s_foo2.c << std::endl; 

    std::cout << s_bar1.a << ", " << 
       s_bar1.b << ", " << 
       s_bar1.c << std::endl; 

    std::cout << s_bar2.a << ", " << 
       s_bar2.b << ", " << 
       s_bar2.c << std::endl; 

    return 0; 
} 

Đây là kết quả:

$ g++ -o ./test ./test.cpp 
$ ./test 
1, 2, 3 
1, 2, 0 
42, 5.88545e-44, 42 
1078523331, 3.14, 1078523331 

Điều duy nhất với khởi tạo C++ là bạn cần phải khởi tạo tất cả các phần tử của cấu trúc hoặc phần còn lại sẽ là initiali zed với số không. Bạn không thể chọn và chọn. Nhưng điều đó vẫn nên được chấp nhận cho trường hợp sử dụng của bạn.

Một điểm bổ sung cần lưu ý: lý do chính cho việc sử dụng trình khởi tạo được chỉ định là sinh lời không phải là thành viên đầu tiên của công đoàn.

Để làm điều đó bạn cần sử dụng "giải pháp thay thế" được hiển thị trong ví dụ nơi tôi đặt thành viên "nổi" bằng cách cung cấp giá trị int tương đương. Đó là một chút của một hack, nhưng nếu nó giải quyết vấn đề của bạn.

+0

Đó là chỉ vì 42L được ngầm đúc đến một số nguyên. Nếu bạn muốn khởi tạo thành viên float, ví dụ, 3.5, bạn không thể làm điều đó trong C++. –

+0

Bạn không thể khởi tạo nhiều thành viên của một công đoàn.Tuy nhiên nếu bạn muốn khởi tạo phần "float", bạn sẽ cần phải khởi tạo nó với số nguyên tương đương (có thể là số hex) – lothar

+6

Có - nhưng các trình khởi tạo được chỉ định của C99 cho phép bạn khởi tạo một phần tử một công đoàn không phải là công ty đầu tiên mà không phải dùng đến các hacks chẳng hạn như tìm ra một số nguyên tương đương với số thực hiện của phao. –

0

báo cáo lỗ khô:

Với

struct S { 
    int mA; 
    int mB; 
    S() {} 
    S(int b} : mB(b) {} // a ctor that does partial initialization 
}; 

tôi đã cố gắng bắt nguồn S1 từ S, nơi constructor mặc định inline S1 của gọi S (int) và vượt qua một giá trị mã hóa cứng ...

struct S1 { 
    S1() : S(22) {} 
} s1; 

... và sau đó được biên dịch bằng gcc 4.0.1 -O2-S. Hy vọng là người tối ưu hóa sẽ thấy rằng s1.mB nhất thiết phải là 22 và gán giá trị nó vào thời gian biên dịch, nhưng từ giá trị đó ...

movl $22, 4+_s1-"L00000000002$pb"(%ebx) 

... có vẻ như mã được tạo ra khởi tạo trong thời gian chạy trước khi chính. Ngay cả khi nó đã làm việc, nó sẽ khó có thể được compilable như C99 và sẽ có kludge của deriving một lớp học cho mỗi đối tượng bạn muốn khởi tạo; vì vậy, đừng bận tâm.

+1

Cảm ơn. Nó không nhất thiết phải biên dịch như C99. Các initializers là ở một số nơi, nhưng chúng được xác định bằng cách sử dụng một số macro, vì vậy một cái gì đó mà tôi có thể ifdef __cplusplus với một sửa đổi tối thiểu, đó là tốt là tốt. –

1
#ifdef __cplusplus 
struct Foo 
{ 
    Foo(int a, int b) : a(a), b(b) {} 
    int a; 
    int b; 
}; 

union Bar 
{ 
    Bar(int a) : a(a) {} 
    Bar(float b) : b(b) {} 
    int a; 
    float b; 
}; 

static Foo foo(1,2); 
static Bar bar1(1); 
static Bar bar2(1.234f); 
#else 
/* C99 stuff */ 
#endif // __cplusplus 

Trong công đoàn C++ cũng có thể có các nhà thầu. Có thể đây là những gì bạn muốn?

+0

Việc khởi tạo được thực hiện vào thời gian chạy hay biên dịch? Đó là, tôi cho rằng, vấn đề quan trọng ở đây. –

+0

Tôi sẽ phải tham khảo tiêu chuẩn holly để chắc chắn, nhưng chỉ cần tắt đầu của tôi, tôi nghĩ rằng tất cả các biến tĩnh toàn cầu được khởi tạo và lưu trữ trong phần dữ liệu của hình ảnh thực thi. Đặt cược tốt nhất của bạn là dùng thử trình biên dịch của bạn và xem nó hoạt động như thế nào. –

+1

Không, không gian sẽ được cấp phát trong hình ảnh thực thi, được khởi tạo thành số không và hàm tạo được gọi là thời gian chạy. Mặc dù vậy, để thêm vào niềm vui, hệ thống nhúng có thể hơi không nhất quán ở điểm cuối cùng này - trong lý thuyết, trình liên kết thu thập danh sách các lệnh gọi hàm dựng tĩnh và sau đó libgcc gọi chúng trong quá trình bootstrapping, nhưng tùy thuộc vào nền tảng của bạn, nó có thể không xảy ra, hoặc chỉ xảy ra nếu bạn chọn các tùy chọn xây dựng phù hợp. – Tom

2

Đây là loại câu trả lời và câu hỏi. Tôi nhận ra chủ đề này đã chết, nhưng chính xác là những gì tôi đã nhìn vào tối nay.

Tôi đã làm một số điều thú vị và điều gần nhất tôi có thể đạt được những gì tôi muốn (tương tự như những gì bạn muốn ... Tôi đã làm việc với các bức ảnh và không cần sử dụng C++, nhưng tôi tò mò nó có thể được thực hiện) là ví dụ mã đầu tiên:

#include <iostream> 

using namespace std; 

extern "C" 
{ 
    typedef struct stuff 
    { 
     int x; 
     double y; 
    } things; 
} 

int main() 
{ 
    things jmcd = { jmcd.x = 12, jmcd.y = 10.1234 }; 
    cout << jmcd.x << " " << jmcd.y << endl; 
    return 0; 
} 

Điều này có vẻ rất giống với kiểu khởi tạo được chỉ định kiểu C99 với báo trước tôi sẽ đề cập sau. (. Bạn có lẽ sẽ quấn này trong #ifdef __cplusplus nếu bạn muốn các cấu trúc được biên soạn bởi một trong hai) Phiên bản thứ hai của mã Tôi nhìn là thế này:

#include <iostream> 

using namespace std; 

extern "C" 
{ 
    typedef struct stuff 
    { 
     int x; 
     double y; 
    } things; 
} 


int main() 
{ 
    things jmcd; 
    jmcd.x = 12; 
    jmcd.y = 10.1234; 
    cout << jmcd.x << " " << jmcd.y << endl; 
    return 0; 
} 

Về cơ bản, từ cách nhìn vào tháo dỡ, nó xuất hiện ví dụ đầu tiên thực sự chậm hơn. Tôi đã xem xét sản lượng lắp ráp và, tốt, tôi phải có một chút gỉ. Có lẽ ai đó có thể cho tôi cái nhìn sâu sắc nào đó. Sản lượng lắp ráp của cpp đầu tiên biên soạn và trông giống như:

main: 
.LFB957: 
    .cfi_startproc 
    .cfi_personality 0x0,__gxx_personality_v0 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    movl %esp, %ebp 
    .cfi_offset 5, -8 
    .cfi_def_cfa_register 5 
    subl $24, %esp 
    movl $0, 12(%esp) 
    movl $0, 16(%esp) 
    movl $0, 20(%esp) 
    movl $12, 12(%esp) 
    movl 12(%esp), %eax 
    movl %eax, 12(%esp) 
    fldl .LC0 
    fstpl 16(%esp) 
    fldl 16(%esp) 
    fstpl 16(%esp) 
    movl 12(%esp), %eax 
    movl %eax, 4(%esp) 
    fildl 4(%esp) 
    fldl 16(%esp) 
    faddp %st, %st(1) 
    fnstcw 2(%esp) 
    movzwl 2(%esp), %eax 
    movb $12, %ah 
    movw %ax, (%esp) 
    fldcw (%esp) 
    fistpl 4(%esp) 
    fldcw 2(%esp) 
    movl 4(%esp), %eax 
    leave 
    ret 
    .cfi_endproc 

Ví dụ thứ hai trông giống như:

main: 
.LFB957: 
    .cfi_startproc 
    .cfi_personality 0x0,__gxx_personality_v0 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    movl %esp, %ebp 
    .cfi_offset 5, -8 
    .cfi_def_cfa_register 5 
    subl $24, %esp 
    movl $12, 12(%esp) 
    fldl .LC0 
    fstpl 16(%esp) 
    movl 12(%esp), %eax 
    movl %eax, 4(%esp) 
    fildl 4(%esp) 
    fldl 16(%esp) 
    faddp %st, %st(1) 
    fnstcw 2(%esp) 
    movzwl 2(%esp), %eax 
    movb $12, %ah 
    movw %ax, (%esp) 
    fldcw (%esp) 
    fistpl 4(%esp) 
    fldcw 2(%esp) 
    movl 4(%esp), %eax 
    leave 
    ret 
    .cfi_endproc 

Cả hai được tạo ra với một lệnh g++ -O0 -S main.cpp. Rõ ràng, ví dụ trực quan kém hiệu quả tạo ra opcode hiệu quả hơn về số lượng các hướng dẫn. Mặt khác, có vài trường hợp mà tôi có thể tưởng tượng ra một vài hướng dẫn rất quan trọng. (Mặt khác, tôi thực sự gặp khó khăn trong việc hiểu lắp ráp không được viết bởi con người, vì vậy có lẽ tôi đang thiếu một cái gì đó ...) Tôi nghĩ rằng điều này cung cấp một giải pháp, mặc dù muộn, cho câu hỏi James hỏi. Điều tiếp theo tôi nên kiểm tra là nếu cùng một khởi tạo được cho phép trong C99; nếu nó hoạt động, tôi nghĩ nó hoàn toàn giải quyết vấn đề của James.

Tuyên bố từ chối trách nhiệm: Tôi không biết liệu công cụ này có hoạt động hay hoạt động tương tự cho bất kỳ trình biên dịch nào khác ngoài g ++ hay không.

+0

Làm việc cho tôi - không phải trong một phần đường dẫn nhanh. Giải pháp tốt hơn so với sử dụng tệp C khác! Upvote! – Sam

+5

Một lần nữa với trễ, nhưng điều này chỉ không hoạt động nếu bạn trao đổi jmcd.x và jmcd.y trong giải pháp đầu tiên. Điều này là do nó không phải là một cấu trúc đặc biệt, nó chỉ là khởi tạo thường xuyên với nhiều biểu thức hơn. Vì vậy, jmcd.x = 12 được thực hiện, và sau đó giá trị kết quả cho biểu thức này (12) được gán cho trường đầu tiên của struct (x). Tương tự cho y. Nếu bạn trao đổi chúng, cả hai trường sẽ là 12. – Asaf

+0

Hỗ trợ cho các trình khởi tạo được chỉ định trong C++ là một phần mở rộng của GNU, và không phải là một phần của C++ 11. Ngay cả với phần mở rộng GCC, việc khởi tạo được chỉ định chỉ được phép cho các loại POD. – chys

18

Xây dựng về câu trả lời Shing Yip, và với lợi ích của thời gian 3 năm, C++ 11 bây giờ có thể đảm bảo thời gian biên dịch khởi tạo:

union Bar 
{ 
    constexpr Bar(int a) : a_(a) {} 
    constexpr Bar(float b) : b_(b) {} 
    int a_; 
    float b_; 
}; 

constexpr Bar bar1(1); 
constexpr Bar bar2(1.234f); 
Các vấn đề liên quan