2009-04-19 11 views
6

Tôi đang làm việc cho một dự án cao cấp cho trường đại học của mình và tôi đã gặp phải một trở ngại lớn trong việc cố gắng làm cho mã của tôi hoạt động.Làm thế nào bạn có thể thực hiện C++ khi trình biên dịch nhúng của bạn không hỗ trợ toán tử mới hoặc STL?

Trình biên dịch mà chúng tôi có cho vi điều khiển Atmel 8 bit của chúng tôi không hỗ trợ toán tử mới hoặc xóa và không hỗ trợ C++ STL. Tôi có thể lập trình nó trong C, nhưng tôi phải thực hiện một thuật toán A * mà tôi chưa bao giờ làm trước đây. Trong khi tôi đã thử C ban đầu, tôi sớm nhận ra rằng tôi chưa bao giờ làm C thuần khiết trước đây. Việc cố gắng mô hình hóa các đối tượng với các cấu trúc và các hàm đang làm chậm tôi xuống vì tôi đã quen với cú pháp C++ sạch hơn nhiều.

Bất kể, từ ngữ chính xác cho các trình biên dịch những thiếu sót của tôi có thể được tìm thấy ở đây: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus

Để vượt qua chúng và vẫn sử dụng C++ Tôi đã xem xét các khả năng sau đây. 1) Không phân bổ bất cứ điều gì, chỉ cần sử dụng mẫu để tạo mảng cố định trên ngăn xếp. 2) Phân bổ và tìm một số hack để gọi hàm tạo cho các đối tượng khi tôi đã phân bổ không gian cho chúng. Vị trí mới không phải là một tùy chọn vì mới không phải là toán tử. 3) Chỉ cần sử dụng C và hút nó lên, một vi điều khiển của nó tại sao tôi nhận được ưa thích? 4) Tìm trình biên dịch tốt hơn có thể sẽ có giá $$$.

Tùy chọn thứ hai là khó nhất nhưng nó sẽ có khoản thanh toán lớn nhất theo cách tôi có thể viết mã này. Tuy nhiên, tôi tưởng tượng rằng gỡ lỗi nó có thể là một nỗi đau lớn nếu tôi làm sai. Tôi đang nghĩ đến việc tạo các đối tượng trên stack, sao chép bit của chúng vào không gian được phân bổ, và sau đó zeroing các bit trong đối tượng để nó không gọi hàm hủy của nó. Để làm điều đó tôi sẽ truy cập các bit trực tiếp với một con trỏ char unsigned và toán tử sizeof để lấy số byte.

Điều đó nghe có vẻ khủng khiếp và tôi không biết nếu nó có thể hoạt động đáng tin cậy, nhưng tôi đang xem xét nó. Tôi biết vtables có thể là một vấn đề nhưng tôi không có ý định về việc có bất kỳ vtables vì ​​nó chỉ là một vi điều khiển 8 bit.

+3

Nếu tôi nhớ chính xác 8 bit Atmels không có đủ ram để nghĩ về bộ nhớ động a'la malloc/new. – lothar

+0

Nếu tôi có thể quá tải các toán tử đó thì tôi có thể kiểm soát bộ nhớ đến từ đâu. Nó có thể được trên đống hoặc ngăn xếp. –

+0

Tôi phát hiện ra rằng tôi chỉ có 1KB để chơi với, mà về cơ bản loại trừ A * như một công cụ tìm đường dẫn khả thi cho robot của tôi. Tôi hiện đang sử dụng một DFS đơn giản cho đến khi tôi có thể nhận được một với nhiều RAM hơn. –

Trả lời

10

Chỉ để ghi lại, việc đánh số bit trong một đối tượng sẽ không ảnh hưởng đến việc destructor được gọi (trừ khi trình biên dịch có một quirk đặc biệt cho phép hành vi này). Chỉ cần viết một số báo cáo khai thác trong destructor của bạn để kiểm tra này ra.

Cấu trúc chương trình của bạn không phân bổ bất kỳ thứ gì có lẽ là cách hệ thống được thiết kế. Tôi đã không làm việc với các hệ thống nhúng trước đây, tuy nhiên tôi đã đọc một số cửa hàng nhúng có kinh nghiệm ngăn cản việc sử dụng bộ nhớ động vì môi trường thời gian chạy có số lượng khan hiếm của nó.


Tuy nhiên, nếu bạn phải, bạn vẫn có thể sử dụng vị trí mới. Nếu bạn không có <new> tiêu đề, đây là những dòng có liên quan trực tiếp từ nó trên phiên bản của tôi về GCC:

// Default placement versions of operator new. 
inline void* operator new(std::size_t, void* __p) throw() { return __p; } 
inline void* operator new[](std::size_t, void* __p) throw() { return __p; } 

// Default placement versions of operator delete. 
inline void operator delete (void*, void*) throw() { } 
inline void operator delete[](void*, void*) throw() { } 

Stick rằng ở đâu đó trong một tập tin header bao gồm bởi tất cả các tập tin nguồn có sử dụng vị trí mới/xóa.

tập tin mẫu mà kiểm tra này:

#include <cstdio> 
#include <new> 

int 
main(int argc, char** argv) 
{ 
    typedef char const* cstr; 
    char foobar[16]; 
    cstr* str = new (&foobar) cstr(argc > 1 ? argv[1] : "Hello, world!"); 
    std::puts(*str); 
    str->~cstr(); 
} 

Trên phiên bản của tôi về GCC, điều này không sử dụng libstdc++ ở tất cả (nếu -fno-exceptions được sử dụng).


Bây giờ, nếu bạn muốn kết hợp điều đó với malloc (nếu nền tảng của bạn cung cấp này), sau đó bạn có thể làm điều này:

#include <cstdio> 
#include <cstdlib> 

inline void* operator new (std::size_t n) {return std::malloc(n);} 
inline void* operator new[](std::size_t n) {return std::malloc(n);} 
inline void operator delete (void* p) {std::free(p);} 
inline void operator delete[](void* p) {std::free(p);} 

int 
main(int argc, char** argv) 
{ 
    typedef char const* cstr; 
    cstr* str = new cstr(argc > 1 ? argv[1] : "Hello, world!"); 
    std::puts(*str); 
    delete str; 
} 

này cho phép bạn sử dụng các tiêu chuẩn new/delete mà bạn quen thuộc với, mà không yêu cầu sử dụng libstdc++.

Chúc may mắn!

+0

Chà, có vẻ như nó hoạt động. Tôi không thể sử dụng các tiêu đề bạn cung cấp nhưng các chức năng điều hành đó dường như hoạt động. Từ những gì tôi đã đọc về những toán tử new [] và delete [] cần phải biết kích thước mảng để gọi tất cả các destructors cần thiết, nhưng trong thử nghiệm của tôi tất cả các destructors cần thiết được gọi. –

0

Tại sao không viết nó trước trên máy tính để bàn, xem xét các giới hạn của trình biên dịch, gỡ lỗi, đảm bảo nó hoạt động hoàn hảo và chỉ sau đó chuyển sang môi trường nhúng?

+0

Tôi không nghĩ đây là một ý tưởng hay vì có rất nhiều hạn chế và sự khác biệt trên nền tảng nhúng (vài RAM, 8 bit thay vì 32 bit) vì vậy bạn sẽ không bao giờ làm việc đó theo cách đó. – mmmmmmmm

23

Đừng chống lại các công cụ của bạn. Nếu trình biên dịch duy nhất bạn có cho hệ thống nhúng của bạn là trình biên dịch C, hãy tìm hiểu C - nó không khó. Cố gắng để sản xuất một số phiên bản bastardised của hai ngôn ngữ chỉ để giải quyết một vấn đề lập trình khá đơn giản sẽ chỉ kết thúc trong nước mắt. Để xem xét nó theo một cách khác, nếu nền tảng nhúng của bạn thậm chí không hỗ trợ trình biên dịch C, nhưng chỉ có một trình biên dịch, thì xung đầu tiên của bạn có phải là ngồi xuống và viết trình biên dịch C++ trong trình biên dịch không? Không. Tôi hy vọng không, tôi hy vọng bạn sẽ ngồi xuống và học cách sử dụng trình biên dịch để hoàn thành nhiệm vụ của bạn - viết trình biên dịch C++ (hoặc thậm chí trình biên dịch C) sẽ hoàn toàn không phù hợp với thời gian của bạn, và gần như chắc chắn sẽ dẫn đến thất bại .

+0

Mặc dù tôi đồng ý rằng các công cụ chiến đấu là ngớ ngẩn, các khoản đầu tư vào phát triển công cụ thường tự trả tiền. Nếu, và khi tôi đã sử dụng assembler writting một tiền xử lý nhỏ dựa trên DSL cũng có giá trị nó. Tuy nhiên tôi không bao giờ đi xa như viết một trình biên dịch C + + :) –

+0

Ồ, tôi đồng ý, và trở lại khi tôi đã viết lắp ráp (một thời gian dài trước đây) tôi đã sử dụng thư viện macro tôi đã viết. Và tôi đã thực sự viết một nửa của một trình biên dịch C trong Z80 assembler (không phải để giải quyết một vấn đề cụ thể mặc dù) trước khi tôi đến giác quan của tôi, viết lại nó trong C và qua biên dịch nó. Nó vẫn không làm việc, nhưng nó đã được ít hơn nhiều công việc :-) –

+0

Và đừng quên rằng các trình biên dịch C nhúng nhất chỉ đến với một tập hợp con rất hạn chế của thư viện C. Một trong những phần quan trọng tôi nghĩ rằng họ thiếu là quản lý đống (malloc/miễn phí). – mmmmmmmm

4

Chỉ vì nó không có những công cụ này không có nghĩa là bạn không thể hưởng lợi từ C++. Nếu dự án đủ lớn, truy cập vào thiết kế hướng đối tượng một mình có thể là động lực đủ.

Nếu nó không hỗ trợ 'mới' thì có thể là do không có sự khác biệt tự động giữa vùng heap và chồng. Điều này có thể là do cấu hình bộ nhớ của bạn. Nó cũng có thể là do tài nguyên bộ nhớ bị hạn chế nên phân bổ rất cẩn thận có ý nghĩa. Nếu bạn hoàn toàn phải triển khai toán tử 'mới' của riêng mình, bạn có thể xem xét điều chỉnh Doug Lea's malloc. Tôi tin rằng ông đã bắt đầu cấp phát của mình trong một tình huống tương tự (reimplementing C++ 's mới).

Tôi thích STL nhưng vẫn có thể thực hiện những nội dung hữu ích mà không cần đến nó. Tùy thuộc vào phạm vi của dự án bạn có thể tốt hơn chỉ bằng cách sử dụng một mảng.

2

Tôi đã có trình biên dịch tương tự đã triển khai phiên bản kỳ lạ của Embedded-C++ standard. Chúng tôi đã có operator new gọi nhà thầu cho chúng tôi và người hủy được gọi là trong hầu hết các trường hợp. Trình biên dịch/nhà cung cấp thời gian chạy đã triển khai trycatch sử dụng setjmplongjmp làm tiện ích cho kỹ sư. Vấn đề là họ không bao giờ đề cập rằng một throw sẽ không gây ra destructors của các đối tượng địa phương được gọi!

Dù sao, nhóm của chúng tôi kế thừa cơ sở mã sau khi ai đó viết một ứng dụng hoạt động giống như Standard C++: sử dụng kỹ thuật RAII và tất cả các tính tốt khác. Chúng tôi đã kết thúc viết lại nó trong những gì một số người trong chúng ta gọi hướng đối tượng C thay thế. Bạn có thể muốn xem xét chỉ cần cắn viên đạn và viết thẳng C. Thay vì các nhà xây dựng, có một phương thức khởi tạo được gọi rõ ràng. Destructors trở thành một phương thức kết thúc rõ ràng. Không có nhiều C++ mà bạn không thể bắt chước trong C khá nhanh chóng. Có, MI là một nỗi đau trong ... nhưng thừa kế duy nhất là khá dễ dàng. Hãy xem this PDF để biết một số ý tưởng. Nó gần như mô tả cách tiếp cận mà chúng tôi đã thực hiện. Tôi thực sự ước tôi đã viết phương pháp của mình ở đâu đó ...

0

khi thực hiện công việc nhúng, tôi thậm chí không thể liên kết thời gian chạy C cho các ràng buộc về bộ nhớ, nhưng phần cứng có hướng dẫn DMA (bộ cấp phát bộ nhớ động) nên tôi đã viết malloc của riêng mình với phần cứng đó, phần cứng của bạn có thể có tính năng tương tự, vì vậy bạn có thể viết một malloc và sau đó một mới dựa trên malloc.

Dù sao cuối cùng tôi đã sử dụng 99% phân bổ ngăn xếp và một vài giới hạn đặt đối tượng tĩnh tĩnh mà tôi sẽ tái chế, bằng cách đặt tại chỗ. Đây có thể là một giải pháp tốt.

6

Tôi nghĩ rằng bạn đang tiếp cận vấn đề từ một quan điểm ít hơn mức tối ưu.

Bạn đang tập trung vào trình biên dịch (hoặc thiếu nó) thay vì tập trung vào HARDWARE.

Câu trả lời có thể xảy ra nhất cho câu hỏi chính của bạn là "bởi vì phần cứng không hỗ trợ tất cả nội dung C++". Phần cứng nhúng (microcontrolers) được chú ý cho việc tùy chỉnh thiết kế phần cứng - bản đồ bộ nhớ, trình xử lý ngắt, I/O, v.v.

Theo ý kiến ​​của tôi, bạn nên dành chút thời gian với sổ phần cứng cho vi điều khiển, học các ins and outs của thiết bị - tức là nó được thiết kế như thế nào và cho mục đích chính nào. Một số được thiết kế cho thao tác bộ nhớ nhanh, một số xử lý I/O nhanh, một số cho công việc kiểu A/D, một số cho xử lý tín hiệu. Các loại vi điều khiển ra lệnh cho các hướng dẫn lắp ráp mà họ đã viết cho nó, và điều đó ra lệnh cho bất kỳ trình biên dịch mức cao hơn nào có thể làm hiệu quả.

Nếu điều này là quan trọng, hãy dành chút thời gian để xem xét công cụ lắp ráp - nó sẽ cho bạn biết những gì các nhà thiết kế coi là quan trọng. Nó cũng sẽ cho bạn biết rất nhiều về số tiền bạn có thể nhận được từ trình biên dịch cấp cao.

Nói chung, vi điều khiển không hỗ trợ C++ vì thiết kế thực sự không quan tâm đến đối tượng hoặc xử lý bộ nhớ ưa thích (từ phối cảnh C++). Nó có thể được thực hiện, nhưng bạn thường cố gắng để đánh một cái chốt tròn trong một lỗ vuông để có được các nhà thầu và những người phá hủy (và 'mới' và 'xóa') để làm việc trong môi trường vi mô.

NẾU bạn có trình biên dịch C cho đơn vị này, hãy xem xét nó là một phước lành. Một trình biên dịch C tốt thường là "quá đủ" để tạo ra phần mềm nhúng tuyệt vời.

Chúc mừng,

-Richard

1

Bạn có thể tìm thấy một số mã hữu ích về A* tutorial website tôi. Mặc dù code Tôi đã viết để hỗ trợ việc sử dụng STL này nên dễ dàng loại bỏ hỗ trợ STL. Ngoài ra có một phân bổ hồ bơi đi kèm với nó (fsa.h) mà tôi đã viết để tăng tốc độ STL trên bảng điều khiển trò chơi. Đó là mã C++, nhưng tôi đã chuyển nó từ C và tôi không nghĩ sẽ khó thực hiện theo cách khác. Mã này được kiểm tra bởi hơn 10.000 người nên đó là cơ sở tốt để bắt đầu.

Thay thế cấu trúc STL tôi đang sử dụng không có vấn đề gì vì nó được giới hạn trong Vectơ. Tôi sử dụng một trong các vectơ làm hàng đợi ưu tiên bằng cách sử dụng các hàm heap (make_heap và push_heap). Bạn có thể thay thế bằng số old C code có hàng đợi ưu tiên được triển khai trong C mà chỉ cần thả vào mã của bạn. (Chỉ có một phân bổ, vì vậy bạn có thể thay thế bằng một con trỏ đến vùng dành riêng cho bộ nhớ của bạn.

Như bạn có thể thấy trong đoạn mã này từ tiêu đề, sự khác biệt chính trong mã C là không có con trỏ, không có đối tượng, vì vậy mã của bạn thường lấy một con trỏ đối tượng làm đối số đầu tiên.

void PQueueInitialise(PQUEUE *pq, int32 MaxElements, uint32 MaxRating, bool32 bIsAscending); 
void PQueueFree(PQUEUE *pq); 
int8 PQueuePush(PQUEUE *pq, void *item, uint32 (*PGetRating) (void *)); 
int32 PQueueIsFull(PQUEUE *pq); 
int32 PQueueIsEmpty(PQUEUE *pq); 
void *PQueuePop(PQUEUE *pq, uint32 (*PGetRating) (void *)); 
+0

"Mã C cũ" của bạn gọi malloc(). Tôi không nghĩ rằng có rất nhiều trình biên dịch C cho nền tảng nhúng 8 bit hỗ trợ đủ của thư viện C để cung cấp cho bạn quản lý đống (malloc/miễn phí)! – mmmmmmmm

+0

Nếu bạn nhìn kỹ hơn, nó gọi malloc để phân bổ một khối bộ nhớ cho hàng đợi ưu tiên có kích thước cố định. Bản thân hàng đợi sau đó sử dụng khối đơn này rất hiệu quả. Vì vậy, nó có thể được chuyển đổi để không sử dụng malloc với một dòng mã. – justinhj

+0

Ai đã bỏ phiếu này? Đây là công cụ hữu ích, tôi cần A * giúp đỡ quá. Tôi có một số mã Google và một cuốn sách về AI nhưng tôi vẫn đang tìm ra tất cả. –