2009-08-30 44 views
17

Tôi biết nó không được hỗ trợ, nhưng tôi tự hỏi nếu có bất kỳ thủ thuật xung quanh nó. Có lời khuyên nào không?Hỗ trợ phản ánh trong C

+0

Tôi thực sự nghi ngờ điều đó. – ChaosPandion

+4

Nếu bạn muốn phản ánh, C và C++ là những ngôn ngữ sai cho bạn. Nó trái với triết lý của họ về "bạn không trả tiền cho những gì bạn không sử dụng." – Crashworks

+0

Bạn có thể nhận được hiệu ứng phản chiếu bằng cách sử dụng các cơ chế bên ngoài cho các ngôn ngữ C/C++. Xem các câu trả lời khác. –

Trả lời

14

Phản ánh nói chung là phương tiện cho một chương trình phân tích cấu trúc của một số mã. Phân tích này được sử dụng để thay đổi hành vi hiệu quả của mã.

Phản ánh như phân tích nói chung rất yếu; thông thường nó chỉ có thể cung cấp quyền truy cập vào chức năng và tên trường. Điểm yếu này đến từ những người triển khai ngôn ngữ về cơ bản không muốn làm cho mã nguồn đầy đủ có sẵn trong thời gian chạy, cùng với các thói quen phân tích thích hợp để trích xuất những gì người ta muốn từ mã nguồn.

Một cách tiếp cận khác là giải quyết phần mềm phân tích chương trình bằng cách sử dụng công cụ phân tích chương trình mạnh, ví dụ: công cụ phân tích cú pháp văn bản nguồn chính xác theo cách trình biên dịch thực hiện. (Thường thì mọi người đề nghị lạm dụng trình biên dịch để làm điều này, nhưng điều đó thường không có tác dụng; máy biên dịch muốn trở thành trình biên dịch và rất khó để uốn cong nó thành các mục đích khác).

Điều cần thiết là một công cụ mà:

  • văn bản nguồn ngôn ngữ phân tích
  • xây dựng cây cú pháp trừu tượng đại diện cho từng chi tiết của chương trình. (Đó là hữu ích nếu ASTs giữ lại ý kiến ​​và các chi tiết khác của cách bố trí mã nguồn như số cột, các giá trị radix đen, vv)
  • xây dựng bảng biểu tượng cho thấy quy mô và ý nghĩa của mỗi định danh
  • có thể trích xuất kiểm soát chảy từ chức năng
  • thể extact luồng dữ liệu từ mã
  • có thể xây dựng một đồ thị cuộc gọi cho hệ thống
  • thể xác định những gì mỗi con trỏ điểm-to
  • cho phép xây dựng các phân tích tùy chỉnh bằng cách sử dụng f trên vi
  • có thể chuyển đổi mã theo phong tục như phân tích (thường là bằng cách sửa đổi ASTs đại diện cho các mã phân tích cú pháp)
  • có thể tái tạo văn bản nguồn (bao gồm bố trí và ý kiến) từ các ASTs sửa đổi.

Sử dụng máy móc như vậy, thực hiện phân tích ở cấp độ chi tiết bất kỳ là cần thiết, sau đó chuyển đổi mã để đạt được hiệu ứng mà thời gian chạy sẽ thực hiện. Có một số lợi ích chính:

  • Mức chi tiết hoặc lượng phân tích là một vấn đề của tham vọng (ví dụ, nó không phải là bị giới hạn bởi những gì phản ánh thời gian chạy chỉ có thể làm)
  • Không có bất kỳ Thời gian chạy trên cao để đạt được thay đổi được phản ánh trong hành vi
  • Máy móc có thể nói chung và được áp dụng trên nhiều ngôn ngữ, thay vì hơn giới hạn đối với những gì triển khai ngôn ngữ cụ thể cung cấp.
  • Điều này tương thích với ý tưởng C/C++ mà bạn không trả tiền cho những gì bạn không sử dụng. Nếu bạn không cần phản ánh, bạn không cần máy móc này. Và ngôn ngữ của bạn không cần phải có hành lý trí tuệ của suy yếu được xây dựng trong.

Xem DMS Software Reengineering Toolkit của chúng tôi cho một hệ thống có thể làm tất cả những điều trên cho C, Java, và COBOL, và hầu hết nó cho C++.

+0

Điều gì sẽ xảy ra nếu bạn không có mã nguồn? – Crashworks

+0

Nếu bạn không có mã nguồn, bạn nên hy vọng rằng phản ánh trả lời câu hỏi cụ thể mà bạn có sẵn có trong hệ thống lập trình của bạn. Vấn đề của OP là về C, mà không có sự phản ánh nào cả. Nếu bạn có một hệ thống với sự phản chiếu, như Java, bạn thường thấy nó sẽ không cung cấp cho bạn đủ chi tiết; Java cung cấp tên và loại tham số chức năng, như OP được yêu cầu? Vấn đề là bạn cần một hệ thống mạnh để phân tích mã và hầu hết các hệ thống/ngôn ngữ lập trình sẽ không cung cấp. Vì vậy, hãy bước ra ngoài và nhận một công cụ * có thể *, không có ngoại lệ. –

+3

Đây là một cách khác để suy nghĩ.Sự phản ánh trong các langauges có nó là về bao nhiêu mã nguồn trình biên dịch sẵn sàng để lại trong mã đối tượng của bạn để cho phép phản chiếu. Trừ khi nó giữ * tất cả * mã nguồn xung quanh, sự phản ánh sẽ bị hạn chế trong khả năng phân tích các sự kiện có sẵn về mã nguồn. –

5

Dựa trên các câu trả lời cho How can I add reflection to a C++ application? (Stack Overflow) và thực tế là C++ được coi là "superset" của C, tôi sẽ nói rằng bạn đã hết may mắn.

Ngoài ra còn có một câu trả lời hay dài về why C++ doesn't have reflection (Stack Overflow).

+0

Bạn không thể sử dụng ngôn ngữ đó. Bạn có thể nhận được hiệu ứng tương đương bằng cách bước ra ngoài ngôn ngữ; xem các câu trả lời khác. –

2

Bạn cần tự mình triển khai nó từ đầu. Trong C thẳng, không có thông tin thời gian chạy được lưu giữ trên cấu trúc và các loại tổng hợp. Siêu dữ liệu đơn giản không tồn tại trong tiêu chuẩn.

+0

Hoặc, nếu bạn thấy thực hiện một hệ thống phản chiếu đầy đủ, bạn có thể sử dụng một công cụ được thiết kế để phân tích/chuyển đổi mã có tất cả các máy móc cần thiết. Xem những câu trả lời khác cho câu hỏi này. –

2
  1. Thực hiện phản ánh cho C sẽ đơn giản hơn nhiều ... vì C là ngôn ngữ đơn giản.
  2. Có một số tùy chọn cơ bản cho chương trình analming, như phát hiện nếu chức năng tồn tại bằng cách gọi dlopen/dlsym - phụ thuộc vào nhu cầu của bạn.
  3. Có các công cụ để tạo mã có thể sửa đổi/mở rộng chính nó bằng cách sử dụng tcc.
  4. Bạn có thể sử dụng công cụ trên để tạo trình phân tích mã của riêng bạn.
+0

bạn có thể tìm thấy các tham số của một hàm, nói sin bằng dlopen/dlsym? và một số máy phân tích mã tốt là gì? – adk

+1

"bạn có thể tìm thấy các tham số của hàm" Không, chỉ có biểu tượng như vậy tồn tại. – Artyom

7

mọi thủ thuật xung quanh nó? Có lời khuyên nào không?

Trình biên dịch có thể sẽ tạo tùy chọn 'tệp biểu tượng gỡ lỗi', trình gỡ lỗi có thể sử dụng để giúp gỡ lỗi mã. Trình liên kết cũng có thể tạo 'tệp bản đồ'.

Bí quyết/mẹo có thể là tạo và sau đó đọc các tệp này.

3

Tôi cần phản ánh trong một nhóm struct s trong dự án C++.
Tôi đã tạo một tệp xml với mô tả về tất cả các cấu trúc đó - may mắn thay các loại trường là kiểu nguyên thủy.
Tôi đã sử dụng mẫu (không phải C++ template) để tự động tạo class cho mỗi struct cùng với các phương thức setter/getter.
Trong mỗi class tôi đã sử dụng một bản đồ để liên kết tên chuỗi và thành viên lớp học (con trỏ đến các thành viên).

Tôi không hối tiếc khi sử dụng sự phản chiếu vì nó đã mở ra các cách mới để thiết kế chức năng cốt lõi của tôi mà tôi thậm chí không thể tưởng tượng mà không phản ánh.
(BTW, nó là một máy phát điện báo cáo bên ngoài cho một chương trình sử dụng một cơ sở dữ liệu liệu)

Vì vậy, tôi đã sử dụng hệ mã, con trỏ hàm và bản đồ để mô phỏng phản ánh.

+0

Vì vậy, bạn đã sử dụng một cách tiếp cận đặc biệt để đạt được hiệu quả mong muốn. Nếu điều đó làm việc cho bạn, tuyệt vời. Một cách tiếp cận có cấu trúc để thực hiện những gì bạn muốn sẽ cho phép bạn bỏ qua mô tả XML mã hóa bằng tay của các cấu trúc đó và "phản ánh" dữ liệu thực tế từ các khai báo struct. Xem các câu trả lời khác cho câu hỏi này. –

+0

Chỉ là những gì tôi đã có trong tâm trí là tốt, sẽ viết một kịch bản PHP phân tích các tập tin .cpp của tôi mà sau đó đổ mã C + +, mà không phản ánh. Tôi cần nó để sử dụng C cấu trúc thông qua một công cụ kịch bản. – lama12345

1

Tôi biết các tùy chọn sau, nhưng tất cả đều theo nguyên giá và rất nhiều hạn chế:

  • Sử dụng libdl (#include <dfcln.h>)
  • Gọi một công cụ như objdump hoặc nm
  • Phân tích các file đối tượng chính bạn (sử dụng thư viện tương ứng)
  • Tham gia phân tích cú pháp và tạo thông tin cần thiết tại thời gian biên dịch.
  • "Lạm dụng" trình liên kết để tạo mảng biểu tượng.

Tôi sẽ sử dụng một chút khung kiểm thử đơn vị làm ví dụ tiếp tục xuống, vì khám phá thử nghiệm tự động cho các khung kiểm tra đơn vị là một ví dụ điển hình, phản ánh rất tiện dụng và đó là điều mà hầu hết các khung kiểm tra đơn vị cho C bị cắt ngắn.

Sử dụng libdl (#include <dfcln.h>) (POSIX)

Nếu bạn đang ở trên một môi trường POSIX, một chút phản xạ có thể được thực hiện bằng libdl. Plugins được phát triển theo cách đó.

Sử dụng

#include <dfcln.h> 

trong mã nguồn của bạn và liên kết với -ldl.

Sau đó, bạn có quyền truy cập vào các chức năng dlopen(), dlerror(), dlsym()dlclose() mà bạn có thể tải và truy cập/chạy đối tượng chia sẻ khi chạy. Tuy nhiên, nó không cho phép bạn dễ dàng truy cập vào bảng biểu tượng.

Một bất lợi của cách tiếp cận này là bạn về cơ bản hạn chế phản ánh đối tượng được tải dưới dạng thư viện động (đối tượng được chia sẻ được tải khi chạy qua dlopen()).

Chạy nm hoặc objdump

Bạn có thể chạy nm hoặc objdump để hiển thị các bảng biểu tượng và phân tích đầu ra. Đối với tôi, nm -P --defined-only -g xyz.o mang lại kết quả tốt và phân tích cú pháp đầu ra là không đáng kể. Bạn chỉ muốn quan tâm đến từ đầu tiên của mỗi dòng, đó là tên biểu tượng và có thể là tên thứ hai, là loại phần.

Nếu bạn không biết tên đối tượng theo cách tĩnh, tức là đối tượng thực sự là đối tượng dùng chung, ít nhất trên Linux, bạn có thể muốn bỏ qua tên biểu tượng bắt đầu bằng '_'.

objdump, nm hoặc các công cụ tương tự cũng thường có sẵn bên ngoài môi trường POSIX.

Phân tích các đối tượng tập tin mình

Bạn có thể phân tích các đối tượng tập tin bản thân. Bạn có thể không muốn thực hiện điều đó từ đầu nhưng sử dụng một thư viện hiện có cho điều đó. Đây là cách nm, objdump và thậm chí libdl được triển khai.Bạn có thể xem mã nguồn của nm, objdumplibdl và các thư viện mà họ sử dụng để tìm hiểu cách họ làm những gì họ làm.

sự tham gia của một Parser

Bạn có thể viết một máy phát điện phân tích cú pháp và mã mà tạo ra các thông tin phản cần thiết tại thời gian biên dịch và lưu trữ nó trong tập tin đối tượng. Sau đó, bạn có rất nhiều tự do và thậm chí có thể triển khai các dạng chú thích nguyên thủy. Đó là những gì một số khung kiểm tra đơn vị như AceUnit làm.

Tôi thấy rằng việc viết một trình phân tích cú pháp bao gồm cú pháp C thẳng về phía trước là khá tầm thường. Viết một trình phân tích cú pháp thực sự hiểu C và có thể đối phó với tất cả các trường hợp là không tầm thường. Vì vậy, điều này có những hạn chế phụ thuộc vào cú pháp C kỳ lạ mà bạn muốn phản ánh như thế nào.

"Lạm dụng" mối liên kết để tạo ra mảng biểu tượng

Bạn có thể đưa tài liệu tham khảo cho các biểu tượng mà bạn muốn phản ánh trong một phần đặc biệt và sử dụng một cấu hình mối liên kết để phát ra các ranh giới phần để bạn có thể truy cập chúng trong C .

Tôi đã mô tả ở đây N-Dependency injection in C - better way than linker-defined arrays? cách thức hoạt động.

Nhưng hãy cẩn thận, điều này phụ thuộc vào rất nhiều thứ và không phải là rất xách tay. Tôi chỉ thử điều này với GCC/ld và tôi biết nó không hoạt động với tất cả các trình biên dịch/trình liên kết. Ngoài ra, nó gần như chắc chắn rằng việc loại bỏ mã chết sẽ không phát hiện ra cách bạn gọi công cụ này, vì vậy nếu bạn sử dụng xóa mã chết, bạn sẽ phải thêm tất cả các biểu tượng được phản ánh làm điểm nhập.

cạm bẫy

Đối với một số cơ chế, mã chết loại bỏ có thể là một vấn đề, đặc biệt là khi bạn "lạm dụng" các mối liên kết để tạo ra một mảng biểu tượng. Nó có thể được làm việc xung quanh bằng cách nói cho các biểu tượng được phản ánh như là điểm nhập vào linker, và tùy thuộc vào số lượng các biểu tượng này có thể không tốt đẹp cũng không thuận tiện.

Kết luận

Kết hợp nmlibdl thực sự có thể cho kết quả khá tốt. Sự kết hợp có thể gần như mạnh mẽ như mức độ Phản ánh được JUnit 3.x sử dụng trong Java. Mức độ phản xạ đưa ra là đủ để thực hiện khung kiểm thử đơn vị kiểu JUnit 3.x cho C, bao gồm phát hiện trường hợp thử nghiệm bằng cách đặt tên quy ước.

Việc liên kết trình phân tích cú pháp hoạt động hiệu quả hơn và giới hạn đối tượng bạn tự biên dịch, nhưng mang lại cho bạn nhiều quyền lực và tự do nhất. Mức độ phản ánh được đưa ra có thể đủ để thực hiện khung kiểm thử đơn vị kiểu đơn vị JUnit 4.x cho C, bao gồm phát hiện trường hợp thử nghiệm bằng chú thích. AceUnit là một khung kiểm tra đơn vị cho C thực hiện chính xác điều này.

Kết hợp phân tích cú pháp và trình liên kết để tạo các mảng biểu tượng có thể cho kết quả rất đẹp - nếu môi trường của bạn nằm dưới sự kiểm soát của bạn mà bạn có thể đảm bảo làm việc với trình liên kết theo cách đó.

Và tất nhiên bạn có thể kết hợp tất cả các phương pháp tiếp cận để ghép các bit và miếng cho đến khi chúng phù hợp với nhu cầu của bạn.

2

Mẹo và thủ thuật luôn tồn tại.Hãy xem thư viện Metaresc https://github.com/alexanderchuranov/Metaresc

Nó cung cấp giao diện cho khai báo kiểu cũng sẽ tạo siêu dữ liệu cho loại. Dựa trên siêu dữ liệu, bạn có thể dễ dàng tuần tự hóa/deserialize các đối tượng của bất kỳ phức tạp. Bạn có thể tuần tự hóa/deserialize XML, JSON, XDR, ký hiệu giống như Lisp, ký hiệu C-init.

Dưới đây là một ví dụ đơn giản:

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 

#include "metaresc.h" 

TYPEDEF_STRUCT (point_t, 
       double x, 
       double y 
       ); 

int main (int argc, char * argv[]) 
{ 
    point_t point = { 
    .x = M_PI, 
    .y = M_E, 
    }; 
    char * str = MR_SAVE_XML (point_t, &point); 
    if (str) 
    { 
     printf ("%s\n", str); 
     free (str); 
    } 
    return (EXIT_SUCCESS); 
} 

Chương trình này sẽ ra

$ ./point 
<?xml version="1.0"?> 
<point> 
    <x>3.1415926535897931</x> 
    <y>2.7182818284590451</y> 
</point> 

Thư viện hoạt động tốt cho gcc mới nhất và kêu vang.

0

Chỉ cần thực hiện bất kỳ loại bàn phím, cây, danh sách liên kết hoặc bất kỳ bộ sưu tập nào bạn cảm thấy thoải mái với việc quản lý hoặc tìm thấy có hiệu quả. Thêm một khóa cho dù chuỗi, loại/id combo hoặc whatnot và điểm địa chỉ đến một chức năng hoặc cấu trúc. Một phiên bản phản chiếu siêu ngây thơ có thể là một bộ sưu tập như sau:

struct reflectable{ 
    size_t size,id,type; // describes payload 
    char* name; 
    void* payload; 
} 

Với trường hợp chuyển đổi lớn, nơi bạn xử lý cho từng loại hoặc tên hoặc macro để đính kèm. Ngoài ra luôn luôn đính kèm các chức năng mà là người nhận của bất cứ điều gì là trong structable phản ánh của bạn, mà là giống như xử lý nhưng với nhiều mô hình công văn được trực tiếp trên container của bạn.