2009-06-04 31 views
8

Để đảm bảo rằng một số mã khởi chạy trước main (sử dụng Arduino/avr-gcc) tôi có mã như sau:Làm thế nào tôi có thể thực hiện khởi tạo trước chính trong C/C++ với avr-gcc?

class Init { 
public: 
    Init() { initialize(); } 
}; 

Init init; 

Lý tưởng nhất là tôi muốn để có thể chỉ đơn giản viết:

initialize(); 

nhưng điều này không biên dịch ...

có cách nào ít tiết để đạt được hiệu quả tương tự?

Lưu ý: mã là một phần của phác họa Arduino nên main chức năng được tự động tạo ra và không thể được sửa đổi (ví dụ để gọi initialize trước khi bất kỳ mã khác).

Cập nhật: lý tưởng là khởi tạo sẽ được thực hiện trong setup chức năng, nhưng trong trường hợp này có mã khác phụ thuộc vào nó mà xảy ra trước main.

Trả lời

11

Bạn có thể sử dụng GCC của constructor attribute để đảm bảo rằng nó được gọi trước khi main():

void Init(void) __attribute__((constructor)); 
void Init(void) { /* code */ } // This will always run before main() 
+0

+1 Đơn giản và gọn gàng. Điều này chắc chắn làm việc trên avr-gcc quá? –

+0

Tôi không biết chắc chắn, vì tôi chưa bao giờ sử dụng avr-gcc, nhưng theo trang này http://www.nongnu.org/avr-libc/user-manual/porting.html, avr-gcc hỗ trợ các loại thuộc tính khác. –

+1

Tôi đã thử điều này bằng cách sử dụng phần mềm Arduino (được hỗ trợ bởi avr-gcc) và nó hoạt động. Tôi sẽ chấp nhận câu trả lời này, cảm ơn bạn. –

2

Giải pháp của bạn đơn giản và sạch sẽ. Những gì bạn có thể làm thêm là đặt mã của bạn trong không gian tên ẩn danh. Tôi không thấy bất kỳ cần phải làm cho nó tốt hơn :)

+0

Bao gói nó trong một không gian tên ẩn danh có lẽ sẽ ngăn chặn tên của lớp và trường hợp của nó gây ô nhiễm không gian tên (đó là một điều tốt) nhưng nó không thực sự giúp làm cho mã ít chi tiết hơn ... –

0

Chắc chắn, bạn đặt điều này trong một trong những tập tin tiêu đề của bạn của bạn, nói preinit.h:

class Init { public: Init() { initialize(); } }; Init init; 

và sau đó, trong một các đơn vị biên dịch của bạn, hãy đặt:

void initialize(void) { 
    // weave your magic here. 
} 
#include "preinit.h" 

tôi biết đó là một kludge nhưng tôi không nhận thức được cách nào di động để làm khởi trước chính mà không sử dụng một constructor lớp thực hiện ở phạm vi tập tin.

Bạn cũng nên cẩn thận bao gồm nhiều hơn một trong các hàm khởi tạo này vì tôi không tin C++ ra lệnh cho đơn hàng - nó có thể là ngẫu nhiên.

Tôi không chắc chắn về điều này "phác thảo" trong đó bạn nói nhưng nó sẽ có thể chuyển đổi các đơn vị biên dịch chính với một kịch bản trước khi có nó truyền cho trình biên dịch, một cái gì đó như:

awk '{print;if (substr($0,0,11) == "int main (") {print "initialize();"};}' 

bạn có thể thấy điều này sẽ ảnh hưởng đến chương trình của bạn vì:

echo '#include <stdio.h> 
int main (void) { 
    int x = 1; 
    return 0; 
}' | awk '{ 
    print; 
    if (substr($0,0,11) == "int main (") { 
     print " initialize();" 
    } 
}' 

tạo ra sau với initialize() gọi thêm:

#include <stdio.h> 
int main (void) { 
    initialize(); 
    int x = 1; 
    return 0; 
} 

Có thể bạn không thể xử lý tệp được tạo trong trường hợp bạn nên bỏ qua tùy chọn cuối cùng đó, nhưng đó là những gì tôi sẽ xem xét trước tiên.

+0

Một giải pháp doesn ' t cần phải được di động. Nó chỉ cần làm việc bằng cách sử dụng avr-gcc. –

0

Sử dụng thành viên tĩnh của các lớp học. Chúng được khởi tạo trước khi nhập vào chính. Điểm bất lợi là bạn không thể kiểm soát thứ tự khởi tạo của các thành viên lớp tĩnh.

Dưới đây là ví dụ của bạn chuyển đổi:

class Init { 
private: 
    // Made the constructor private, so to avoid calling it in other situation 
    // than for the initialization of the static member. 
    Init() { initialize(); } 

private: 
    static Init INIT; 
}; 


Init Init::INIT; 
+3

Đó là chi tiết hơn, không ít hơn. –

+0

Ý tưởng này làm cho tôi tự hỏi nếu thứ tự khởi tạo của các thành viên tĩnh trên lớp * phân cấp * được định nghĩa trong C + +? Nếu vậy đó sẽ là một cách để giải quyết "fiasco thứ tự khởi tạo tĩnh". –

+0

@Piotr: Thứ tự khởi tạo trong đơn vị dịch được định nghĩa là "thứ tự các định nghĩa xuất hiện". Giữa các đơn vị dịch không xác định. –

2

Nếu bạn đang sử dụng môi trường Arduino, là có lý do nào đó bạn không thể đặt nó trong setup method?

Tất nhiên, đây là sau khi thiết lập phần cứng Arduino cụ thể, vì vậy nếu bạn có công cụ cấp thấp như vậy mà nó thực sự phải đi trước main, thì bạn cần một số ma thuật xây dựng.

UPDATE:

Ok, nếu nó phải được thực hiện trước khi chính tôi nghĩ rằng cách duy nhất là sử dụng một constructor như bạn đã làm.

Bạn luôn có thể thực hiện một macro tiền xử lý của nó:

#define RUN_EARLY(code) \ 
namespace { \ 
    class Init { \ 
     Init() { code; } \ 
    }; \ 
    Init init; \ 
} 

Bây giờ này nên làm việc:

RUN_EARLY(initialize()) 

Nhưng nó không thực sự làm cho mọi thứ ngắn hơn, chỉ cần di chuyển mã tiết xung quanh.

+0

+1, nhưng tiếc là tôi không thể sử dụng thiết lập() cho việc này. Tôi sẽ cập nhật câu hỏi để làm rõ điều này. –

4

Bạn có thể thực hiện trên rất nhẹ ngắn hơn bằng cách cho "khởi tạo" một kiểu trả về và sử dụng mà để khởi tạo một biến toàn cầu:

int initialize(); 
int dummy = initialize(); 

Tuy nhiên, bạn cần phải cẩn thận với điều này, tiêu chuẩn thực hiện không đảm bảo rằng việc khởi tạo ở trên (hoặc một cho đối tượng init của bạn) diễn ra trước khi chính được chạy (3.6.2/3):

Nó được xác định thực hiện khởi động động (8.5, 9.4 , 12.1, 12.6.1) của một đối tượng của phạm vi không gian tên được thực hiện trước khi tuyên bố đầu tiên của mai n.

Điều duy nhất được đảm bảo là việc khởi chạy sẽ diễn ra trước khi 'giả' được sử dụng.

Tùy chọn xâm nhập hơn (nếu có thể) có thể sử dụng "-D main = avr_main" trong makefile của bạn. Sau đó, bạn có thể thêm chính của chính mình như sau:

// Add a declaration for the main declared by the avr compiler. 
int avr_main (int argc, const char * argv[]); // Needs to match exactly 

#undef main 
int main (int argc, const char * argv[]) 
{ 
    initialize(); 
    return avr_main (argc, argv); 
} 

Ít nhất ở đây bạn được đảm bảo rằng việc khởi tạo sẽ diễn ra khi bạn mong đợi.

+0

Thật không may là môi trường Arduino kiểm soát việc thực hiện trình biên dịch để không có makefile mà tôi có thể thay đổi. –

3

Dưới đây là một phương pháp hơi ác để đạt được điều này:

#include <stdio.h> 

static int bar = 0; 

int __real_main(int argc, char **argv); 

int __wrap_main(int argc, char **argv) 
{ 
    bar = 1; 
    return __real_main(argc, argv); 
} 

int main(int argc, char **argv) 
{ 
    printf("bar %d\n",bar); 
    return 0; 
} 

Thêm dòng sau vào cờ mối liên kết: --wrap main

ví dụ.

gcc -Xlinker --wrap -Xlinker main a.c 

Các mối liên kết sẽ thay thế tất cả các cuộc gọi đến main với các cuộc gọi đến __wrap_main, xem ld man page trên --wrap

+0

Thật không may là tôi không có quyền kiểm soát dòng lệnh gcc (thực sự avr-gcc) vì điều này được xử lý tự động bởi môi trường Arduino. –

2

Bạn có thể sử dụng các phần ".init *" để thêm mã C để chạy trước khi main() (và cả thời gian chạy C). Các phần này được liên kết vào tệp thực thi ở cuối và được gọi vào thời điểm cụ thể trong khi khởi tạo chương trình. Bạn có thể lấy danh sách ở đây:

http://www.nongnu.org/avr-libc/user-manual/mem_sections.html

.init1 ví dụ là yếu ràng buộc __init(), vì vậy nếu bạn xác định __init(), nó sẽ được liên kết và được gọi là điều đầu tiên. Tuy nhiên, ngăn xếp chưa được thiết lập, vì vậy bạn phải cẩn thận trong những gì bạn làm (chỉ sử dụng biến register8_t, không gọi bất kỳ chức năng nào).

0

Có cách tôi thực hiện mã hóa chính trước. Có các đoạn init cắt ngang được thực hiện trước chính, tham chiếu đến các phần http://www.nongnu.org/avr-libc/user-manual/mem_sections.html initN.

Nhưng dù sao, điều này chỉ hoạt động trên tối ưu hóa -O0 vì một lý do nào đó. Tôi vẫn cố gắng tìm ra tùy chọn "tối ưu hóa" mã lắp ráp chính trước của tôi.

static void 
__attribute__ ((naked)) 
__attribute__ ((section (".init8"))) /* run this right before main */ 
__attribute__ ((unused)) /* Kill the unused function warning */ 
stack_init(void) {assembly stuff} 

Cập nhật, hóa ra tôi đã xác nhận chức năng này không được sử dụng, dẫn đến tối ưu hóa thói quen. Tôi đã có ý định giết chức năng cảnh báo không sử dụng. Nó được cố định để sử dụng thuộc tính được sử dụng thay thế.

Các vấn đề liên quan