Làm cách nào để thêm phản chiếu vào ứng dụng C ++?


263

Tôi muốn có thể hướng nội một lớp C ++ cho tên, nội dung của nó (tức là thành viên và loại của họ), v.v. Tôi đang nói về C ++ bản địa ở đây, không được quản lý C ++, có phản ánh. Tôi nhận ra C ++ cung cấp một số thông tin hạn chế sử dụng RTTI. Những thư viện bổ sung (hoặc các kỹ thuật khác) có thể cung cấp thông tin này?


18
Thật may mắn, bạn không thể làm điều đó mà không có macro và tiền xử lý khác, vì siêu dữ liệu cần thiết không tồn tại trừ khi bạn tự tạo nó thông qua một số phép thuật tiền xử lý macro.
jalf

6
Thông tin bạn có thể lấy lại từ RTTI không đủ để thực hiện hầu hết những điều bạn thực sự muốn phản ánh. Bạn không thể lặp lại các chức năng thành viên của một lớp chẳng hạn.
Joseph Garvin

Câu trả lời:


259

Những gì bạn cần làm là để bộ tiền xử lý tạo dữ liệu phản ánh về các trường. Dữ liệu này có thể được lưu trữ dưới dạng các lớp lồng nhau.

Đầu tiên, để làm cho nó dễ dàng và sạch sẽ hơn để viết nó trong bộ tiền xử lý, chúng ta sẽ sử dụng biểu thức gõ. Một biểu thức gõ chỉ là một biểu thức đặt kiểu trong ngoặc đơn. Vì vậy, thay vì viết int xbạn sẽ viết (int) x. Dưới đây là một số macro tiện dụng để trợ giúp với các biểu thức được nhập:

#define REM(...) __VA_ARGS__
#define EAT(...)

// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x

Tiếp theo, chúng tôi xác định một REFLECTABLEmacro để tạo dữ liệu về từng trường (cộng với chính trường đó). Macro này sẽ được gọi như thế này:

REFLECTABLE
(
    (const char *) name,
    (int) age
)

Vì vậy, bằng cách sử dụng Boost.PP, chúng tôi lặp lại qua từng đối số và tạo dữ liệu như thế này:

// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
    typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};


#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

Điều này không tạo ra một hằng số fields_nlà số trường có thể phản xạ trong lớp. Sau đó, nó chuyên field_datacho từng lĩnh vực. Nó cũng kết bạn với reflectorlớp, điều này là để nó có thể truy cập vào các trường ngay cả khi chúng ở chế độ riêng tư:

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }

    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

Bây giờ để lặp lại qua các trường, chúng tôi sử dụng mẫu khách truy cập. Chúng tôi tạo phạm vi MPL từ 0 đến số lượng trường và truy cập dữ liệu trường tại chỉ mục đó. Sau đó, nó chuyển dữ liệu trường cho khách truy cập do người dùng cung cấp:

struct field_visitor
{
    template<class C, class Visitor, class I>
    void operator()(C& c, Visitor v, I)
    {
        v(reflector::get_field_data<I::value>(c));
    }
};


template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}

Bây giờ cho khoảnh khắc của sự thật, chúng tôi đặt tất cả lại với nhau. Đây là cách chúng ta có thể định nghĩa một Personlớp có thể phản ánh:

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    REFLECTABLE
    (
        (const char *) name,
        (int) age
    )
};

Đây là một print_fieldshàm tổng quát sử dụng dữ liệu phản chiếu để lặp lại qua các trường:

struct print_visitor
{
    template<class FieldData>
    void operator()(FieldData f)
    {
        std::cout << f.name() << "=" << f.get() << std::endl;
    }
};

template<class T>
void print_fields(T & x)
{
    visit_each(x, print_visitor());
}

Một ví dụ về việc sử dụng print_fieldsvới Personlớp có thể phản chiếu :

int main()
{
    Person p("Tom", 82);
    print_fields(p);
    return 0;
}

Đầu ra nào:

name=Tom
age=82

Và voila, chúng tôi vừa thực hiện phản chiếu trong C ++, với hơn 100 dòng mã.


106
Kudos cho thấy cách thực hiện phản xạ, thay vì nói nó không thể được thực hiện. Câu trả lời như thế này làm cho SO trở thành một nguồn tài nguyên tuyệt vời.
sợ hãi_fool

4
Lưu ý rằng nếu bạn cố gắng biên dịch cái này trong Visual Studio, bạn sẽ gặp lỗi vì VS không xử lý mở rộng macro biến đổi đúng cách. Đối với VS, hãy thử thêm: #define DETAIL_TYPEOF_INT2(tuple) DETAIL_TYPEOF_HEAD tuple#define DETAIL_TYPEOF_INT(...) DETAIL_TYPEOF_INT2((__VA_ARGS__)) thay đổi định nghĩa TYPEOF (x) thành:#define TYPEOF(x) DETAIL_TYPEOF_INT(DETAIL_TYPEOF_PROBE x,)
Phenglei Kai

Tôi nhận được lỗi 'BOOST_PP_IIF_0' không đặt tên cho một loại. Bạn có thể vui lòng giúp đỡ.
Ankit Zalani

3
Xem câu trả lời của riêng tôi - stackoverflow.com/a/28399807/2338477 Tôi đã trích xuất và đóng gói lại tất cả các định nghĩa, và thư viện boost không cần thiết. Là mã demo tôi đang cung cấp tuần tự hóa cho xml và khôi phục từ xml.
TarmoPikaro

106

Có hai loại reflectionbơi xung quanh.

  1. Kiểm tra bằng cách lặp lại các thành viên của một loại, liệt kê các phương thức của nó và như vậy.

    Điều này là không thể với C ++.
  2. Kiểm tra bằng cách kiểm tra xem một loại lớp (lớp, struct, union) có một phương thức hoặc kiểu lồng nhau, có nguồn gốc từ một loại cụ thể khác không.

    Loại điều này là có thể với C ++ bằng cách sử dụng template-tricks. Sử dụng boost::type_traitscho nhiều thứ (như kiểm tra xem một loại là tích phân). Để kiểm tra sự tồn tại của hàm thành viên, hãy sử dụng Có thể viết mẫu để kiểm tra sự tồn tại của hàm không? . Để kiểm tra xem có tồn tại một loại lồng nhau nào đó hay không, hãy sử dụng SFINAE đơn giản .

Nếu bạn đang tìm cách để thực hiện 1), như tìm kiếm một lớp có bao nhiêu phương thức, hoặc thích lấy biểu diễn chuỗi của id lớp, thì tôi e rằng không có cách nào để thực hiện điều này. Bạn phải sử dụng một trong hai

  • Trình biên dịch Meta như Trình biên dịch đối tượng Qt Meta dịch mã của bạn thêm thông tin meta bổ sung.
  • Một khung bao gồm các macro cho phép bạn thêm các thông tin meta cần thiết. Bạn sẽ cần nói cho khung công tác tất cả các phương thức, tên lớp, lớp cơ sở và mọi thứ nó cần.

C ++ được thực hiện với tốc độ trong tâm trí. Nếu bạn muốn kiểm tra mức cao, như C # hoặc Java có, thì tôi e rằng tôi phải nói với bạn rằng không có cách nào mà không có nỗ lực.


122
C ++ được tạo ra với tốc độ trong tâm trí, nhưng triết lý không phải là "nhanh nhất có thể", thay vào đó, "bạn không trả tiền cho nó nếu bạn không sử dụng nó." Tôi tin rằng ngôn ngữ có thể thực hiện nội tâm theo cách phù hợp với triết lý đó, C ++ chỉ thiếu nó.
Joseph Garvin

8
@Joseph: Làm thế nào nên được thực hiện? Nó yêu cầu tất cả các siêu dữ liệu được lưu trữ. Điều đó có nghĩa là bạn phải trả tiền cho nó, ngay cả khi bạn không sử dụng nó. (Trừ khi bạn có thể đánh dấu các loại riêng lẻ là "phản chiếu hỗ trợ", nhưng sau đó chúng tôi gần như xuống nơi chúng tôi cũng có thể sử dụng thủ thuật vĩ mô hiện có.
jalf

25
@jalf: Chỉ siêu dữ liệu có thể cần thiết. Nếu chúng ta chỉ xem xét sự phản ánh thời gian biên dịch, thì đây là chuyện nhỏ. Ví dụ: hàm thời gian biên dịch members<T>trả về danh sách tất cả các thành viên của T. Nếu chúng ta muốn có phản xạ thời gian chạy (tức là RTTI trộn với phản xạ), trình biên dịch vẫn sẽ biết tất cả các loại cơ sở được phản ánh. Rất có khả năng members<T>(T&)sẽ không bao giờ được khởi tạo cho T = std :: chuỗi, vì vậy không bao gồm RTTI cho chuỗi std :: hoặc các lớp dẫn xuất của nó.
MSalters

9
Thư viện phản xạ (được đề cập dưới đây) thêm phản chiếu vào C ++ mà không làm chậm mã hiện có tại: root.cern.ch/drupal/content/reflex
Joseph Lisee

6
@Joe: Sự phản chiếu không bao giờ làm chậm mã hiện có. Nó chỉ làm cho công cụ được phân phối lớn hơn (vì bạn phải cung cấp cơ sở dữ liệu thông tin loại ...).
mmmmmmmm

56

Và tôi sẽ yêu một con ngựa, nhưng ngựa con không miễn phí. :-p

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI là những gì bạn sẽ nhận được. Phản ánh như bạn đang nghĩ về - siêu dữ liệu mô tả đầy đủ có sẵn trong thời gian chạy - chỉ không tồn tại cho C ++ theo mặc định.


1
Tôi thứ hai Brad. Các mẫu C ++ có thể khá mạnh mẽ và có nhiều kinh nghiệm xung quanh các hành vi loại 'phản xạ' khác nhau, chẳng hạn như thư viện 'bất kỳ', đặc điểm loại, C ++ RTTI, v.v. có thể giải quyết nhiều vấn đề phản ánh được giải quyết. Vậy Nick, mục tiêu của bạn ở đây là gì?
Aaron

7
Upvote cho nhận xét ngựa con! Tôi đã nâng cấp hai lần, vì câu trả lời của bạn cũng xứng đáng, nhưng thật đáng buồn là tôi chỉ nhận được một, vì vậy ngựa con chiến thắng. :-)
Franci Penov

6
Tôi không thực sự hiểu tại sao đây là một phản ứng thông minh. Tôi đã nói rằng tôi muốn tham khảo các thư viện vv để thực hiện điều này. Sự phản chiếu / hướng nội là dành cho nhiều hệ thống khác nhau để cho phép truy cập tập lệnh, tuần tự hóa, v.v.
Nick

3
@Nick: Anh ấy đã trả lời rồi. Không thể thực hiện được, dữ liệu không tồn tại và do đó, không có thư viện nào có thể thực hiện nó cho bạn.
jalf

@jalf Vẫn còn lạ đối với tôi khi đọc những người trong thế giới lập trình nói rằng nghĩ rằng 'không thể nào' và không 'Tôi không biết làm thế nào'. Chắc chắn siêu dữ liệu không tồn tại nhưng có thể được chèn bằng macro
Freddx L.

38

Thông tin tồn tại - nhưng không phải ở định dạng bạn cần và chỉ khi bạn xuất các lớp. Điều này hoạt động trong Windows, tôi không biết về các nền tảng khác. Sử dụng các chỉ định lớp lưu trữ như trong, ví dụ:

class __declspec(export) MyClass
{
public:
    void Foo(float x);
}

Điều này làm cho trình biên dịch xây dựng dữ liệu định nghĩa lớp vào DLL / exe. Nhưng nó không ở định dạng mà bạn có thể dễ dàng sử dụng để phản chiếu.

Tại công ty của tôi, chúng tôi đã xây dựng một thư viện diễn giải siêu dữ liệu này và cho phép bạn phản ánh một lớp mà không cần chèn thêm macro, v.v. vào chính lớp đó. Nó cho phép các hàm được gọi như sau:

MyClass *instance_ptr=new MyClass;
GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);

Điều này có hiệu quả:

instance_ptr->Foo(1.331);

Hàm Invoke (this_pulum, ...) có các đối số biến. Rõ ràng bằng cách gọi một hàm theo cách này, bạn sẽ tránh được những thứ như const-safe, v.v., vì vậy những khía cạnh này được thực hiện như kiểm tra thời gian chạy.

Tôi chắc chắn cú pháp có thể được cải thiện và cho đến nay nó chỉ hoạt động trên Win32 và Win64. Chúng tôi đã thấy nó thực sự hữu ích khi có giao diện GUI tự động cho các lớp, tạo các thuộc tính trong C ++, truyền phát đến và từ XML, v.v. và không cần phải xuất phát từ một lớp cơ sở cụ thể. Nếu có đủ nhu cầu, có lẽ chúng ta có thể biến nó thành hình để phát hành.


1
Tôi nghĩ bạn có ý nghĩa __declspec(dllexport)và bạn có thể lấy thông tin từ tệp .map nếu bạn cho phép tạo như vậy trong quá trình xây dựng.
Orwellophile

18

Sự phản chiếu không được hỗ trợ bởi C ++. Điều này thật đáng buồn vì nó làm cho việc kiểm tra phòng thủ trở thành một nỗi đau.

Có một số cách tiếp cận để thực hiện phản ánh:

  1. sử dụng thông tin gỡ lỗi (không di động).
  2. Rắc mã của bạn bằng macro / mẫu hoặc một số cách tiếp cận nguồn khác (trông xấu xí)
  3. Sửa đổi trình biên dịch như clang / gcc để tạo cơ sở dữ liệu.
  4. Sử dụng phương pháp Qt moc
  5. Tăng phản xạ
  6. Phản xạ chính xác và phẳng

Liên kết đầu tiên có vẻ hứa hẹn nhất (sử dụng mod để kêu vang), liên kết thứ hai thảo luận về một số kỹ thuật, thứ ba là một cách tiếp cận khác bằng cách sử dụng gcc:

  1. http://www.donw.org/rfl/

  2. https://bitbucket.org/dwilliamson/clreflect

  3. https://root.cern.ch/how/how-use-reflex

Hiện tại có một nhóm làm việc để phản ánh C ++. Xem tin tức cho C ++ 14 @ Cern:

Chỉnh sửa ngày 13/08/17:

Kể từ bài viết gốc, đã có một số tiến bộ tiềm năng về sự phản ánh. Sau đây cung cấp thêm chi tiết và thảo luận về các kỹ thuật và trạng thái khác nhau:

  1. Phản xạ tĩnh trong một Nutshell
  2. Phản xạ tĩnh
  3. Một thiết kế cho sự phản chiếu tĩnh

Tuy nhiên, có vẻ không hứa hẹn về cách tiếp cận phản xạ được tiêu chuẩn hóa trong C ++ trong tương lai gần trừ khi có sự quan tâm nhiều hơn từ cộng đồng để hỗ trợ cho phản ánh trong C ++.

Các chi tiết sau đây về trạng thái hiện tại dựa trên phản hồi từ cuộc họp tiêu chuẩn C ++ cuối cùng:

Chỉnh sửa ngày 13/12/2017

Sự phản chiếu dường như đang hướng tới C ++ 20 hoặc nhiều khả năng là TSR. Chuyển động tuy nhiên chậm.

Chỉnh sửa 15/09/2018

Một dự thảo TS đã được gửi đến các cơ quan quốc gia để bỏ phiếu.

Văn bản có thể được tìm thấy ở đây: https://github.com/cplusplus/reflection-ts

Chỉnh sửa 11/07/2019

Phản ánh TS là tính năng hoàn chỉnh và được đưa ra để bình luận và bỏ phiếu trong mùa hè (2019).

Cách tiếp cận lập trình mẫu meta sẽ được thay thế bằng cách tiếp cận mã thời gian biên dịch đơn giản hơn (không được phản ánh trong TS).

Chỉnh sửa 10/02/2020

Có một yêu cầu hỗ trợ TS phản chiếu trong Visual Studio tại đây:

Nói về TS của tác giả David Sankel:

Chỉnh sửa ngày 17 tháng 3 năm 2020

Tiến bộ về sự phản ánh đang được thực hiện. Có thể tìm thấy báo cáo từ '2020/02 Prague Báo cáo chuyến đi của Ủy ban ISO C ++' tại đây:

Chi tiết về những gì đang được xem xét cho C ++ 23 có thể được tìm thấy ở đây (bao gồm phần ngắn về Reflection):

Chỉnh sửa ngày 4 tháng 6 năm 2020

Một khung mới đã được Jeff Preshing phát hành có tên là 'Ván ép' có chứa cơ chế phản ánh thời gian chạy. Nhiều thông tin thêm có thế được tìm thấy ở đây:

Các công cụ và cách tiếp cận có vẻ là bóng bẩy nhất và dễ sử dụng nhất cho đến nay.


1
Liên kết cern bị hỏng.
Mostowski sụp đổ

liên kết cern nên được sửa chữa ngay bây giờ. Họ có xu hướng phá vỡ khá thường xuyên đó là một nỗi đau.
Damian Dixon

Có phải câu trả lời này chỉ liên quan đến sự phản ánh thời gian biên dịch?
einpoklum

@einpoklum các giải pháp hiện tại duy nhất cho sự phản chiếu là thời gian biên dịch, thường là với mã siêu mẫu hoặc macro. Bản nháp mới nhất TS có vẻ như nó sẽ hoạt động trong thời gian chạy nhưng bạn sẽ phải xây dựng tất cả các thư viện với trình biên dịch chính xác cho siêu dữ liệu cần thiết được lưu trữ.
Damian Dixon

@DamianDixon: Điều đó không đúng. Có một số thư viện phản ánh thời gian chạy. Bây giờ, được cho phép, chúng khá cồng kềnh và là tùy chọn hoặc yêu cầu các trình biên dịch, nhưng chúng vẫn tồn tại. Nếu, như tôi hiểu nhận xét của bạn, bạn chỉ đề cập đến phản ánh thời gian biên dịch, vui lòng chỉnh sửa câu trả lời của bạn để làm cho nó rõ ràng hơn.
einpoklum

15

Bạn cần nhìn vào những gì bạn đang cố gắng làm, và nếu RTTI sẽ đáp ứng yêu cầu của bạn. Tôi đã thực hiện phản ánh giả của riêng mình cho một số mục đích rất cụ thể. Ví dụ, tôi đã từng muốn có thể cấu hình linh hoạt những gì một mô phỏng sẽ tạo ra. Nó yêu cầu thêm một số mã soạn sẵn cho các lớp sẽ là đầu ra:

namespace {
  static bool b2 = Filter::Filterable<const MyObj>::Register("MyObject");
} 

bool MyObj::BuildMap()
{
  Filterable<const OutputDisease>::AddAccess("time", &MyObj::time);
  Filterable<const OutputDisease>::AddAccess("person", &MyObj::id);
  return true;
}

Cuộc gọi đầu tiên thêm đối tượng này vào hệ thống lọc, gọi BuildMap()phương thức này để tìm ra phương thức nào khả dụng.

Sau đó, trong tệp cấu hình, bạn có thể làm một cái gì đó như thế này:

FILTER-OUTPUT-OBJECT   MyObject
FILTER-OUTPUT-FILENAME file.txt
FILTER-CLAUSE-1        person == 1773
FILTER-CLAUSE-2        time > 2000

Thông qua một số phép thuật mẫu liên quan boost, điều này được dịch thành một loạt các lệnh gọi phương thức vào thời gian chạy (khi tệp cấu hình được đọc), vì vậy nó khá hiệu quả. Tôi không khuyên bạn nên làm điều này trừ khi bạn thực sự cần, nhưng, khi bạn làm, bạn có thể làm một số thứ thực sự tuyệt vời.


Phải yêu những chức năng này luôn luôn trả về đúng;) Tôi cho rằng điều này là miễn dịch với các vấn đề đặt hàng init tĩnh?
paulm

14

Tôi khuyên bạn nên sử dụng Qt .

Có giấy phép nguồn mở cũng như giấy phép thương mại.


1
Tôi đã xem xét điều này nhưng nó sử dụng các macro và mã nguồn cần phân tích cú pháp để tạo mã dữ liệu meta. Tôi muốn tránh bước thêm này. Tôi muốn sử dụng thư viện C ++ hoặc các macro đơn giản. Cảm ơn ý tưởng mặc dù.
Nick

10
QT, hoặc một thư viện khác thực hiện một cách tiếp cận tương tự là cách tốt nhất bạn sẽ có được
jalf

5
Thanh toán theo thời gian biên dịch hoặc thanh toán khi chạy - theo cách bạn đang trả!
Martin Beckett

13

Bạn đang cố gắng làm gì với sự phản ánh?
Bạn có thể sử dụng các đặc điểm loại Boost và thư viện typeof như một hình thức phản ánh thời gian biên dịch giới hạn. Đó là, bạn có thể kiểm tra và sửa đổi các thuộc tính cơ bản của một loại được truyền cho một mẫu.


13

EDIT : CAMP không còn được duy trì; hai dĩa có sẵn:

  • Một cái cũng được gọi là CAMP và dựa trên cùng một API.
  • Ponder là một phần viết lại, và sẽ được ưu tiên vì nó không yêu cầu Boost; đó là sử dụng C ++ 11.

CAMP là một thư viện được MIT cấp phép (trước đây là LGPL) có thêm sự phản chiếu cho ngôn ngữ C ++. Nó không yêu cầu một bước tiền xử lý cụ thể trong quá trình biên dịch, nhưng ràng buộc phải được thực hiện thủ công.

Thư viện Tegesoft hiện tại sử dụng Boost, nhưng cũng có một fork sử dụng C ++ 11 không còn yêu cầu Boost .


11

Tôi đã làm một cái gì đó giống như những gì bạn sau một lần và trong khi có thể nhận được một số mức độ phản chiếu và truy cập vào các tính năng cấp cao hơn, thì vấn đề đau đầu về bảo trì có thể không đáng. Hệ thống của tôi đã được sử dụng để giữ cho các lớp UI tách biệt hoàn toàn với logic nghiệp vụ thông qua việc ủy ​​quyền giống như khái niệm truyền và chuyển tiếp thông điệp của Objective-C. Cách thực hiện là tạo một số lớp cơ sở có khả năng ánh xạ các ký hiệu (Tôi đã sử dụng một chuỗi chuỗi nhưng bạn có thể làm điều đó với enums nếu bạn thích tốc độ và xử lý lỗi thời gian biên dịch trên tổng độ linh hoạt) cho các con trỏ hàm (thực tế thì không con trỏ chức năng thuần túy, nhưng một cái gì đó tương tự như Boost có với Boost.Factor - lúc đó tôi không có quyền truy cập). Bạn có thể làm điều tương tự cho các biến thành viên của mình miễn là bạn có một số lớp cơ sở chung có khả năng đại diện cho bất kỳ giá trị nào. Toàn bộ hệ thống là sự phân tách không mã hóa của Mã hóa và giá trị khóa, với một vài tác dụng phụ có lẽ đáng giá thời gian cần thiết để khiến mọi lớp sử dụng hệ thống phù hợp với tất cả các phương thức và thành viên của nó với các cuộc gọi hợp pháp : 1) Bất kỳ lớp nào cũng có thể gọi bất kỳ phương thức nào trên bất kỳ lớp nào khác mà không phải bao gồm các tiêu đề hoặc viết các lớp cơ sở giả để giao diện có thể được xác định trước cho trình biên dịch; và 2) Các getters và setters của các biến thành viên rất dễ làm cho luồng an toàn vì việc thay đổi hoặc truy cập các giá trị của chúng luôn được thực hiện thông qua 2 phương thức trong lớp cơ sở của tất cả các đối tượng. Toàn bộ hệ thống là sự phân tách không mã hóa của Mã hóa và giá trị khóa, với một vài tác dụng phụ có lẽ đáng giá thời gian cần thiết để khiến mọi lớp sử dụng hệ thống phù hợp với tất cả các phương thức và thành viên của nó với các cuộc gọi hợp pháp : 1) Bất kỳ lớp nào cũng có thể gọi bất kỳ phương thức nào trên bất kỳ lớp nào khác mà không phải bao gồm các tiêu đề hoặc viết các lớp cơ sở giả để giao diện có thể được xác định trước cho trình biên dịch; và 2) Các getters và setters của các biến thành viên rất dễ làm cho luồng an toàn vì việc thay đổi hoặc truy cập các giá trị của chúng luôn được thực hiện thông qua 2 phương thức trong lớp cơ sở của tất cả các đối tượng. Toàn bộ hệ thống là sự phân tách không mã hóa của Mã hóa và giá trị khóa, với một vài tác dụng phụ có lẽ đáng giá thời gian cần thiết để khiến mọi lớp sử dụng hệ thống phù hợp với tất cả các phương thức và thành viên của nó với các cuộc gọi hợp pháp : 1) Bất kỳ lớp nào cũng có thể gọi bất kỳ phương thức nào trên bất kỳ lớp nào khác mà không phải bao gồm các tiêu đề hoặc viết các lớp cơ sở giả để giao diện có thể được xác định trước cho trình biên dịch; và 2) Các getters và setters của các biến thành viên rất dễ làm cho luồng an toàn vì việc thay đổi hoặc truy cập các giá trị của chúng luôn được thực hiện thông qua 2 phương thức trong lớp cơ sở của tất cả các đối tượng. 1) Bất kỳ lớp nào cũng có thể gọi bất kỳ phương thức nào trên bất kỳ lớp nào khác mà không cần phải bao gồm các tiêu đề hoặc viết các lớp cơ sở giả để giao diện có thể được xác định trước cho trình biên dịch; và 2) Các getters và setters của các biến thành viên rất dễ làm cho luồng an toàn vì việc thay đổi hoặc truy cập các giá trị của chúng luôn được thực hiện thông qua 2 phương thức trong lớp cơ sở của tất cả các đối tượng. 1) Bất kỳ lớp nào cũng có thể gọi bất kỳ phương thức nào trên bất kỳ lớp nào khác mà không cần phải bao gồm các tiêu đề hoặc viết các lớp cơ sở giả để giao diện có thể được xác định trước cho trình biên dịch; và 2) Các getters và setters của các biến thành viên rất dễ làm cho luồng an toàn vì việc thay đổi hoặc truy cập các giá trị của chúng luôn được thực hiện thông qua 2 phương thức trong lớp cơ sở của tất cả các đối tượng.

Nó cũng dẫn đến khả năng thực hiện một số điều thực sự kỳ lạ mà không dễ dàng trong C ++. Ví dụ, tôi có thể tạo một đối tượng Array chứa các mục tùy ý thuộc bất kỳ loại nào, bao gồm cả chính nó và tạo các mảng mới một cách linh hoạt bằng cách chuyển một thông báo tới tất cả các mục mảng và thu thập các giá trị trả về (tương tự như ánh xạ trong Lisp). Một cách khác là việc thực hiện quan sát giá trị khóa, nhờ đó tôi có thể thiết lập UI để phản hồi ngay lập tức với các thay đổi trong các thành viên của lớp phụ trợ thay vì liên tục thăm dò dữ liệu hoặc vẽ lại màn hình một cách không cần thiết.

Có thể thú vị hơn với bạn là thực tế là bạn cũng có thể kết xuất tất cả các phương thức và các thành viên được xác định cho một lớp và ở dạng chuỗi không hơn không kém.

Nhược điểm của hệ thống có thể khiến bạn không bận tâm: thêm tất cả các thông báo và khóa-giá trị là vô cùng tẻ nhạt; nó chậm hơn không có bất kỳ sự phản ánh nào; bạn sẽ trở nên ghét nhìn thấy boost::static_pointer_castboost::dynamic_pointer_cast trên tất cả các cơ sở mã của bạn với một niềm đam mê mãnh liệt; những hạn chế của hệ thống gõ mạnh vẫn còn đó, bạn thực sự chỉ cần che giấu chúng một chút để nó không rõ ràng. Typose trong chuỗi của bạn cũng không phải là một niềm vui hoặc dễ dàng để khám phá bất ngờ.

Về cách thực hiện một cái gì đó như thế này: chỉ cần sử dụng các con trỏ được chia sẻ và yếu cho một số cơ sở chung (cái của tôi được gọi là "Object" một cách tưởng tượng) và lấy ra cho tất cả các loại bạn muốn sử dụng. Tôi khuyên bạn nên cài đặt Boost.Factor thay vì thực hiện theo cách tôi đã làm, với một số crap tùy chỉnh và một tấn macro xấu xí để bọc các lệnh gọi con trỏ hàm. Vì mọi thứ đều được ánh xạ, việc kiểm tra các đối tượng chỉ là vấn đề lặp qua tất cả các phím. Vì các lớp học của tôi về cơ bản gần với sự tách ra trực tiếp của Ca cao nhất có thể khi chỉ sử dụng C ++, nếu bạn muốn một cái gì đó tương tự thì tôi khuyên bạn nên sử dụng tài liệu về Cacao như một bản thiết kế.


Này, @Michael; Bạn vẫn có mã nguồn cho việc này, hoặc bạn đã thoát khỏi nó? Tôi muốn xem thử nếu bạn không phiền.
RandomDSdevel

Rất tiếc, đánh vần sai tên của bạn! Không có gì ngạc nhiên khi tôi không bao giờ nhận được câu trả lời
RandomDSdevel

10

Có một thư viện mới để phản chiếu trong C ++, được gọi là RTTR (Run Time Type Reflection, xem thêm github ).

Giao diện tương tự như sự phản chiếu trong C # và nó hoạt động mà không cần bất kỳ RTTI nào.


8

Hai giải pháp giống như phản xạ mà tôi biết từ những ngày C ++ của mình là:

1) Sử dụng RTTI, sẽ cung cấp bootstrap để bạn xây dựng hành vi giống như phản xạ của mình, nếu bạn có thể khiến tất cả các lớp của mình xuất phát từ lớp cơ sở của 'đối tượng'. Lớp đó có thể cung cấp một số phương thức như GetMethod, GetBaseClass, v.v.

2) Một tùy chọn khác, nếu bạn có quyền truy cập vào các đối tượng trình biên dịch là sử dụng DIA SDK . Nếu tôi nhớ chính xác, điều này cho phép bạn mở pdbs, trong đó sẽ chứa siêu dữ liệu cho các loại C ++ của bạn. Nó có thể là đủ để làm những gì bạn cần. Trang này cho thấy cách bạn có thể lấy tất cả các loại cơ sở của một lớp chẳng hạn.

Cả hai giải pháp này là một chút xấu xí mặc dù! Không có gì giống như một chút của C ++ để khiến bạn đánh giá cao sự xa xỉ của C #.

Chúc may mắn.


Đó là xảo quyệt và một vụ hack khổng lồ, với điều DIA SDK mà bạn đề xuất ở đó.
Sqeaky

7

EDIT: Cập nhật liên kết bị hỏng kể từ tháng 2, 7, 2017.

Tôi nghĩ không ai đề cập đến điều này:

Tại CERN, họ sử dụng hệ thống phản chiếu đầy đủ cho C ++:

Phản xạ Cern . Nó dường như làm việc rất tốt.


@ j4nbur53 Liên kết bị hỏng vì dường như họ đã đạt được một cột mốc: root.cern.ch
Germán Diago

Có thể là bạn có nghĩa là liên kết này root.cern.ch/root/doc/ROOTUsersGuideHTML/ch07.html Phản xạ chương?
Mostowski sụp đổ

Hãy thử root.cern.ch/how/how-use-reflex này . Reflex hoạt động như một trình tạo phân tích cú pháp các tệp tiêu đề của bạn và tạo mã / thư viện nội suy c ++, mà bạn có thể liên kết và sử dụng một api đơn giản.
Adam Ryczkowski

6

Câu hỏi này bây giờ hơi cũ (không biết tại sao tôi tiếp tục trả lời các câu hỏi cũ ngày hôm nay) nhưng tôi đã suy nghĩ về BOOST_FUSION_ADAPT_STRVEL giới thiệu sự phản ánh thời gian biên dịch.

Tất nhiên, tùy thuộc vào bản đồ này để phản ánh thời gian chạy, và nó sẽ không quá dễ dàng, nhưng có thể theo hướng này, trong khi nó sẽ không ngược lại :)

Tôi thực sự nghĩ rằng một macro để đóng gói BOOST_FUSION_ADAPT_STRUCTcái có thể tạo ra các phương thức cần thiết để có được hành vi thời gian chạy.


2
bởi minghua (người ban đầu chỉnh sửa bài đăng): Tôi đã tìm hiểu giải pháp BOOST_FUSION_ADAPT_STRVEL này và cuối cùng đã đưa ra một ví dụ. Xem câu hỏi SO mới hơn này - C ++ lặp lại trong trường cấu trúc lồng nhau với boost fusion Adapt_struct .
Matthieu M.

Tuyệt vời, Matthieu! Chỉ cần nhận ra đã thấy gợi ý của bạn ở đây và ở đó trong suốt năm qua. Không nhận thấy họ có liên quan đến bây giờ. Đó là những cảm hứng rất lớn.
minghua

6

Tôi nghĩ rằng bạn có thể thấy thú vị bài viết "Sử dụng mẫu để phản chiếu trong C ++" của Dominic Filion. Nó nằm trong phần 1.4 của Đá quý lập trình trò chơi 5 . Thật không may, tôi không có bản sao của mình, nhưng hãy tìm nó vì tôi nghĩ nó giải thích những gì bạn đang yêu cầu.


4

Ponder là một thư viện phản ánh C ++, để trả lời cho câu hỏi này. Tôi đã xem xét các lựa chọn và quyết định tự làm vì tôi không thể tìm thấy cái nào đánh dấu vào tất cả các hộp của mình.

Mặc dù có câu trả lời tuyệt vời cho câu hỏi này, tôi không muốn sử dụng hàng tấn macro hoặc dựa vào Boost. Boost là một thư viện tuyệt vời, nhưng có rất nhiều dự án C ++ 0x bespoke nhỏ đơn giản hơn và có thời gian biên dịch nhanh hơn. Ngoài ra còn có những lợi thế để có thể trang trí một lớp bên ngoài, như gói thư viện C ++ không (chưa?) Hỗ trợ C ++ 11. Đây là một nhánh của CAMP, sử dụng C ++ 11, không còn yêu cầu Boost .


4

Sự phản chiếu về cơ bản là về những gì trình biên dịch quyết định để lại dưới dạng dấu chân trong mã mà mã thời gian chạy có thể truy vấn. C ++ nổi tiếng vì không trả tiền cho những gì bạn không sử dụng; bởi vì hầu hết mọi người không sử dụng / muốn phản ánh, trình biên dịch C ++ sẽ tránh được chi phí bằng cách không ghi lại bất cứ điều gì .

Vì vậy, C ++ không cung cấp sự phản chiếu và không dễ để "mô phỏng" nó theo quy tắc chung như các câu trả lời khác đã lưu ý.

Trong "các kỹ thuật khác", nếu bạn không có ngôn ngữ với sự phản chiếu, hãy lấy một công cụ có thể trích xuất thông tin bạn muốn tại thời điểm biên dịch.

Bộ công cụ tái cấu trúc phần mềm DMS của chúng tôi là công nghệ trình biên dịch tổng quát được tham số hóa bằng các định nghĩa langauge rõ ràng. Nó có các định nghĩa ngôn ngữ cho C, C ++, Java, COBOL, PHP, ...

Đối với các phiên bản C, C ++, Java và COBOL, nó cung cấp quyền truy cập đầy đủ vào các cây phân tích và thông tin bảng biểu tượng. Thông tin bảng biểu tượng đó bao gồm loại dữ liệu bạn có thể muốn từ "phản chiếu". Nếu mục tiêu của bạn là liệt kê một số trường hoặc phương thức và làm một cái gì đó với chúng, DMS có thể được sử dụng để chuyển đổi mã theo những gì bạn tìm thấy trong các bảng ký hiệu theo cách tùy ý.


3

Bạn có thể tìm thấy một thư viện khác tại đây: http://www.garret.ru/cppreflection/docs/reflect.html Nó hỗ trợ 2 cách: lấy thông tin loại từ thông tin gỡ lỗi và cho phép lập trình viên cung cấp thông tin này.

Tôi cũng thích phản ánh cho dự án của mình và tìm thấy thư viện này, tôi chưa thử nó, nhưng đã thử các công cụ khác từ anh chàng này và tôi thích cách họ làm việc :-)


3

Kiểm tra Classdesc http: // classdesc.sf.net . Nó cung cấp sự phản chiếu dưới dạng "mô tả" lớp, hoạt động với bất kỳ trình biên dịch C ++ tiêu chuẩn nào (vâng, nó được biết là hoạt động với Visual Studio cũng như GCC) và không yêu cầu chú thích mã nguồn (mặc dù một số pragma tồn tại để xử lý các tình huống khó khăn ). Nó đã được phát triển trong hơn một thập kỷ, và được sử dụng trong một số dự án quy mô công nghiệp.


1
Chào mừng bạn đến với Stack Overflow. Mặc dù câu trả lời này thuộc chủ đề, nhưng điều quan trọng là chỉ ra rằng bạn là tác giả của phần mềm này, để làm cho nó rõ ràng không phải là một đề xuất khách quan :-)
Matthew Strawbridge

2

Khi tôi muốn phản ánh trong C ++, tôi đã đọc bài viết này và cải thiện những gì tôi thấy ở đó. Xin lỗi, không thể có. Tôi không sở hữu kết quả ... nhưng bạn chắc chắn có thể nhận được những gì tôi đã có và đi từ đó.

Tôi hiện đang nghiên cứu, khi tôi cảm thấy thích nó, các phương pháp sử dụng inherit_linearly để làm cho định nghĩa về các loại có thể phản xạ dễ dàng hơn nhiều. Tôi đã đi khá xa trong đó nhưng tôi vẫn có cách để đi. Những thay đổi trong C ++ 0x rất có thể sẽ giúp ích rất nhiều trong lĩnh vực này.


2

Có vẻ như C ++ vẫn chưa có tính năng này. Và C ++ 11 cũng bị hoãn phản ánh ((

Tìm kiếm một số macro hoặc làm cho riêng. Qt cũng có thể giúp phản xạ (nếu nó có thể được sử dụng).


2

mặc dù sự phản chiếu không được hỗ trợ ngoài luồng trong c ++, nhưng nó không quá khó để thực hiện. Tôi đã gặp bài viết tuyệt vời này: http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

Bài viết giải thích rất chi tiết về cách bạn có thể thực hiện một hệ thống phản ánh khá đơn giản và thô sơ. cho rằng nó không phải là giải pháp tốt nhất, và có những cạnh thô còn lại để được sắp xếp nhưng đối với nhu cầu của tôi thì nó là đủ.

điểm mấu chốt - sự phản chiếu có thể được đền đáp nếu được thực hiện một cách chính xác và nó hoàn toàn khả thi trong c ++.


2

Tôi muốn quảng cáo sự tồn tại của bộ công cụ hướng nội / phản xạ tự động "IDK". Nó sử dụng một trình biên dịch meta như Qt's và thêm thông tin meta trực tiếp vào các tệp đối tượng. Nó được tuyên bố là dễ sử dụng. Không phụ thuộc bên ngoài. Nó thậm chí còn cho phép bạn tự động phản ánh chuỗi std :: và sau đó sử dụng nó trong các tập lệnh. Hãy nhìn vào IDK


2

Nếu bạn đang tìm kiếm sự phản chiếu C ++ tương đối đơn giản - tôi đã thu thập từ nhiều nguồn macro / định nghĩa khác nhau và nhận xét chúng về cách chúng hoạt động. Bạn có thể tải xuống các tệp tiêu đề từ đây:

https://github.com/tapika/TestCppReflect/blob/master/MacroHelpers.h

bộ định nghĩa, cộng với chức năng trên đầu trang của nó:

https://github.com/tapika/TestCppReflect/blob/master/CppReflect.h https://github.com/tapika/TestCppReflect/blob/master/CppReflect.cpp https://github.com/tapika/TestCpp blob / master / TypeTraits.h

Ứng dụng mẫu cũng nằm trong kho git, tại đây: https://github.com/tapika/TestCppReflect/

Tôi sẽ sao chép một phần ở đây với lời giải thích:

#include "CppReflect.h"
using namespace std;


class Person
{
public:

    // Repack your code into REFLECTABLE macro, in (<C++ Type>) <Field name>
    // form , like this:

    REFLECTABLE( Person,
        (CString)   name,
        (int)       age,
...
    )
};

void main(void)
{
    Person p;
    p.name = L"Roger";
    p.age = 37;
...

    // And here you can convert your class contents into xml form:

    CStringW xml = ToXML( &p );
    CStringW errors;

    People ppl2;

    // And here you convert from xml back to class:

    FromXml( &ppl2, xml, errors );
    CStringA xml2 = ToXML( &ppl2 );
    printf( xml2 );

}

REFLECTABLEđịnh nghĩa sử dụng tên lớp + tên trường với offsetof- để xác định vị trí trong trường cụ thể của bộ nhớ. Tôi đã cố gắng chọn thuật ngữ .NET càng nhiều càng tốt, nhưng C ++ và C # thì khác nhau, vì vậy nó không phải là 1 đến 1. Mô hình phản chiếu toàn bộ C ++ nằm trong TypeInfoFieldInfocác lớp.

Tôi đã sử dụng trình phân tích cú pháp pugi xml để tìm nạp mã demo vào xml và khôi phục lại từ xml.

Vì vậy, đầu ra được sản xuất bởi mã demo trông như thế này:

<?xml version="1.0" encoding="utf-8"?>
<People groupName="Group1">
    <people>
        <Person name="Roger" age="37" />
        <Person name="Alice" age="27" />
        <Person name="Cindy" age="17" />
    </people>
</People>

Bạn cũng có thể kích hoạt bất kỳ hỗ trợ cấu trúc / lớp bên thứ 3 nào thông qua lớp TypeTraits và đặc tả mẫu một phần - để xác định lớp TypeTraitsT của riêng bạn, theo cách tương tự như CString hoặc int - xem mã ví dụ trong

https://github.com/tapika/TestCppReflect/blob/master/TypeTraits.h#L195

Giải pháp này được áp dụng cho Windows / Visual studio. Có thể chuyển nó sang hệ điều hành / trình biên dịch khác, nhưng chưa thực hiện được. (Hỏi tôi nếu bạn thực sự thích giải pháp, tôi có thể giúp bạn)

Giải pháp này có thể áp dụng cho một tuần tự bắn của một lớp với nhiều lớp con.

Tuy nhiên, nếu bạn đang tìm kiếm cơ chế để tuần tự hóa các phần của lớp hoặc thậm chí để kiểm soát các cuộc gọi phản ánh chức năng tạo ra, bạn có thể xem giải pháp sau:

https://github.com/tapika/cppscriptcore/tree/master/SolutionProjectModel

Thông tin chi tiết có thể được tìm thấy từ video youtube:

Phản xạ kiểu thời gian chạy C ++ https://youtu.be/TN8tJijkeFE

Tôi đang cố gắng giải thích sâu hơn một chút về cách hoạt động của phản xạ c ++.

Mã mẫu sẽ trông giống như ví dụ này:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/testCppApp.cpp

c.General.IntDir = LR"(obj\$(ProjectName)_$(Configuration)_$(Platform)\)";
c.General.OutDir = LR"(bin\$(Configuration)_$(Platform)\)";
c.General.UseDebugLibraries = true;
c.General.LinkIncremental = true;
c.CCpp.Optimization = optimization_Disabled;
c.Linker.System.SubSystem = subsystem_Console;
c.Linker.Debugging.GenerateDebugInformation = debuginfo_true;

Nhưng mỗi bước ở đây thực sự dẫn đến việc gọi hàm Sử dụng các thuộc tính C ++ với __declspec(property(get =, put ... ) .

trong đó nhận thông tin đầy đủ về Kiểu dữ liệu C ++, tên thuộc tính C ++ và con trỏ cá thể lớp, dưới dạng đường dẫn và dựa trên thông tin đó bạn có thể tạo xml, json hoặc thậm chí tuần tự hóa nó qua internet.

Ví dụ về các hàm gọi lại ảo như vậy có thể được tìm thấy ở đây:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/VCConfiguration.cpp

Xem các chức năng ReflectCopyvà chức năng ảo::OnAfterSetProperty .

Nhưng vì chủ đề thực sự tiên tiến - tôi khuyên bạn nên kiểm tra qua video trước.

Nếu bạn có một số ý tưởng cải tiến, hãy liên hệ với tôi.


1

Sự phản chiếu trong C ++ rất hữu ích, trong trường hợp bạn cần chạy một số phương thức cho từng thành viên (Ví dụ: tuần tự hóa, băm, so sánh). Tôi đến với giải pháp chung chung, với cú pháp rất đơn giản:

struct S1
{
    ENUMERATE_MEMBERS(str,i);
    std::string str;
    int i;
};
struct S2
{
    ENUMERATE_MEMBERS(s1,i2);
    S1 s1;
    int i2;
};

Trong đó ENUMERATE_MEMBERS là một macro, được mô tả sau (CẬP NHẬT):

Giả sử chúng ta đã định nghĩa hàm serialization cho int và std :: string như thế này:

void EnumerateWith(BinaryWriter & writer, int val)
{
    //store integer
    writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
    //store string
    writer.WriteBuffer(val.c_str(), val.size());
}

Và chúng ta có chức năng chung gần "macro bí mật";)

template<typename TWriter, typename T>
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T>
{
    val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}

Bây giờ bạn có thể viết

S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");

EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)

Vì vậy, có macro ENUMERATE_MEMBERS trong định nghĩa cấu trúc, bạn có thể xây dựng tuần tự hóa, so sánh, băm và các nội dung khác mà không cần chạm vào loại gốc, yêu cầu duy nhất là thực hiện phương thức "Enum CảWith" cho mỗi loại, không thể đếm được, theo liệt kê (như BinaryWriter) . Thông thường, bạn sẽ phải thực hiện 10-20 loại "đơn giản" để hỗ trợ bất kỳ loại nào trong dự án của bạn.

Macro này nên có chi phí không cần thiết để tạo / hủy cấu trúc trong thời gian chạy và mã của T.Enum CảWith () phải được tạo theo yêu cầu, có thể đạt được bằng cách tạo thành hàm nội tuyến mẫu, do đó chỉ có chi phí trong tất cả câu chuyện là thêm ENUMERATE_MEMBERS (m1, m2, m3 ...) cho mỗi cấu trúc, trong khi thực hiện phương thức cụ thể cho mỗi loại thành viên là điều bắt buộc trong bất kỳ giải pháp nào, vì vậy tôi không coi đó là chi phí chung.

CẬP NHẬT: Việc triển khai macro ENUMERATE_MEMBERS rất đơn giản (tuy nhiên có thể được mở rộng một chút để hỗ trợ kế thừa từ cấu trúc có thể đếm được)

#define ENUMERATE_MEMBERS(...) \
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }

// EnumerateWithHelper
template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v) 
{ 
    int x[] = { (EnumerateWith(enumerator, v), 1)... }; 
}

// Generic EnumerateWith
template<typename TEnumerator, typename T>
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))>
{
    val.EnumerateWith(enumerator);
}

Và bạn không cần bất kỳ thư viện bên thứ 3 nào cho 15 dòng mã này;)


1

Bạn có thể đạt được các tính năng phản xạ tĩnh tuyệt vời cho các cấu trúc với BOOST_HANA_DEFINE_STRVEL từ thư viện Boost :: Hana.
Hana khá đa năng, không chỉ cho usecase mà bạn có trong đầu mà còn cho rất nhiều mẫu siêu lập trình.


1

Các Reflection Random Access thư viện làm cho khá dễ dàng và phản ánh trực quan - tất cả các thông tin lĩnh vực / loại được thiết kế để một trong hai có sẵn trong mảng hoặc cảm thấy như truy cập mảng. Nó được viết cho C ++ 17 và hoạt động với Visual Studios, g ++ và Clang. Thư viện chỉ là tiêu đề, có nghĩa là bạn chỉ cần sao chép "Reflect.h" vào dự án của bạn để sử dụng nó.

Các cấu trúc hoặc lớp được phản ánh cần macro REFLECT, nơi bạn cung cấp tên của lớp bạn đang phản ánh và tên của các trường.

class FuelTank {
    public:
        float capacity;
        float currentLevel;
        float tickMarks[2];

    REFLECT(() FuelTank, () capacity, () currentLevel, () tickMarks)
};

Đó là tất cả, không cần thêm mã để thiết lập sự phản chiếu. Tùy chọn, bạn có thể cung cấp các siêu lớp (trong ngoặc đơn của đối số đầu tiên) và các chú thích trường (trong ngoặc đơn dẫn đến trường bạn muốn chú thích) để có thể duyệt qua các siêu lớp hoặc thêm thông tin thời gian biên dịch bổ sung vào một trường (như Json: :Làm lơ).

Vòng qua các lĩnh vực có thể đơn giản như ...

for ( size_t i=0; i<FuelTank::Class::TotalFields; i++ )
    std::cout << FuelTank::Class::Fields[i].name << std::endl;

Bạn có thể lặp qua một đối tượng để truy cập các giá trị trường (mà bạn có thể đọc hoặc sửa đổi) và thông tin loại trường ...

FuelTank::Class::ForEachField(fuelTank, [&](auto & field, auto & value) {
    using Type = typename std::remove_reference<decltype(value)>::type;
    std::cout << TypeToStr<Type>() << " " << field.name << ": " << value << std::endl;
});

Một thư viện JSON được xây dựng trên đầu trang của RandomAccessReflection sẽ xác định tự động đại diện đầu ra JSON thích hợp cho việc đọc hoặc viết, và đệ quy có thể đi qua bất kỳ lĩnh vực phản ánh, cũng như mảng và STL container.

struct MyOtherObject { int myOtherInt; REFLECT(() MyOtherObject, () myOtherInt) };
struct MyObject
{
    int myInt;
    std::string myString;
    MyOtherObject myOtherObject;
    std::vector<int> myIntCollection;

    REFLECT(() MyObject, () myInt, () myString, (Reflected) myOtherObject, () myIntCollection)
};

int main()
{
    MyObject myObject = {};
    std::cout << "Enter MyObject:" << std::endl;
    std::cin >> Json::in(myObject);
    std::cout << std::endl << std::endl << "You entered:" << std::endl;
    std::cout << Json::pretty(myObject);
}

Ở trên có thể được chạy như vậy ...

Enter MyObject:
{
  "myInt": 1337, "myString": "stringy", "myIntCollection": [2,4,6],
  "myOtherObject": {
    "myOtherInt": 9001
  }
}


You entered:
{
  "myInt": 1337,
  "myString": "stringy",
  "myOtherObject": {
    "myOtherInt": 9001
  },
  "myIntCollection": [ 2, 4, 6 ]
}

Xem thêm...


0

Nếu bạn khai báo một con trỏ tới một hàm như thế này:

int (*func)(int a, int b);

Bạn có thể gán một vị trí trong bộ nhớ cho chức năng đó như thế này (yêu cầu libdldlopen)

#include <dlfcn.h>

int main(void)
{
    void *handle;
    char *func_name = "bla_bla_bla";
    handle = dlopen("foo.so", RTLD_LAZY);
    *(void **)(&func) = dlsym(handle, func_name);
    return func(1,2);
}

Để tải một biểu tượng cục bộ bằng cách sử dụng gián tiếp, bạn có thể sử dụng dlopentrên nhị phân gọi ( argv[0]).

Yêu cầu duy nhất cho điều này (trừ dlopen(), libdldlfcn.h) được biết các đối số và kiểu của hàm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.