문제

C++ 클래스의 이름, 내용(예:회원 및 유형) 등여기서는 리플렉션이 있는 관리되는 C++가 아니라 네이티브 C++를 이야기하고 있습니다.C++에서는 RTTI를 사용하여 일부 제한된 정보를 제공한다는 것을 알고 있습니다.이 정보를 제공할 수 있는 추가 라이브러리(또는 기타 기술)는 무엇입니까?

도움이 되었습니까?

해결책 17

숙고하다 이 질문에 대한 답변은 C++ 리플렉션 라이브러리입니다.나는 옵션을 고려한 후 내 조건을 모두 충족하는 옵션을 찾을 수 없었기 때문에 직접 만들기로 결정했습니다.

이 질문에 대한 훌륭한 답변이 있지만 저는 수많은 매크로를 사용하거나 Boost에 의존하고 싶지 않습니다.Boost는 훌륭한 라이브러리이지만 더 간단하고 컴파일 시간이 더 빠른 소규모 맞춤형 C++0x 프로젝트가 많이 있습니다.(아직?) C++11을 지원하지 않는 C++ 라이브러리를 래핑하는 것과 같이 외부에서 클래스를 장식할 수 있다는 장점도 있습니다.C++11을 사용하는 CAMP의 포크입니다. 더 이상 부스트가 필요하지 않습니다..

다른 팁

당신이 해야 할 일은 전처리기가 필드에 대한 반사 데이터를 생성하도록 하는 것입니다.이 데이터는 중첩 클래스로 저장될 수 있습니다.

먼저 전처리기에서 더 쉽고 깔끔하게 작성하기 위해 형식화된 표현을 사용하겠습니다.형식화된 표현식은 유형을 괄호 안에 넣는 표현식일 뿐입니다.그래서 글을 쓰는 것보다 int x 당신은 쓸 것입니다 (int) x.다음은 입력된 표현식에 도움이 되는 몇 가지 편리한 매크로입니다.

#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

다음으로, 우리는 REFLECTABLE 각 필드(및 필드 자체)에 대한 데이터를 생성하는 매크로입니다.이 매크로는 다음과 같이 호출됩니다.

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

그래서 사용 부스트.PP 각 인수를 반복하고 다음과 같은 데이터를 생성합니다.

// 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)); \
    } \
}; \

이것이 하는 일은 상수를 생성하는 것입니다. fields_n 이는 클래스의 반영 가능한 필드 수입니다.그러면 전문적으로 field_data 각 분야마다.그것은 또한 친구 reflector 클래스를 사용하면 비공개인 경우에도 필드에 액세스할 수 있습니다.

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;
    };
};

이제 필드를 반복하기 위해 방문자 패턴을 사용합니다.0부터 필드 수까지의 MPL 범위를 생성하고 해당 인덱스의 필드 데이터에 액세스합니다.그런 다음 필드 데이터를 사용자가 제공한 방문자에게 전달합니다.

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));
}

이제 진실의 순간을 위해 우리는 모든 것을 하나로 모았습니다.다음은 우리가 Person 반영 가능한 클래스:

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

다음은 일반화된 print_fields 반사 데이터를 사용하여 필드를 반복하는 함수:

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());
}

사용 예 print_fields 반사판과 함께 Person 수업:

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

출력은 다음과 같습니다.

name=Tom
age=82

그리고 짜잔, 우리는 C++에서 100줄 미만의 코드로 리플렉션을 구현했습니다.

두 종류가 있습니다 reflection 수영.

  1. 유형의 멤버를 반복하고 해당 메소드를 열거하는 등의 검사를 수행합니다.

    C++에서는 불가능합니다.
  2. 클래스 유형(class, struct, Union)에 메소드 또는 중첩 유형이 있는지 확인하여 다른 특정 유형에서 파생되었는지 검사합니다.

    이런 종류의 일은 C++를 사용하여 가능합니다. template-tricks.사용 boost::type_traits 많은 경우(유형이 필수인지 확인하는 등)멤버 함수의 존재 여부를 확인하려면 다음을 사용하세요. 함수의 존재를 확인하는 템플릿을 작성할 수 있습니까? .특정 중첩 유형이 존재하는지 확인하려면 일반을 사용하십시오. SFINAE .

클래스에 얼마나 많은 메서드가 있는지 확인하거나 클래스 ID의 문자열 표현을 가져오는 등 1)을 수행하는 방법을 찾고 있다면 이 작업을 수행하는 표준 C++ 방식이 없을 것 같습니다.둘 중 하나를 사용해야합니다

  • 추가 메타 정보를 추가하여 코드를 변환하는 Qt Meta Object Compiler와 같은 메타 컴파일러입니다.
  • 필요한 메타 정보를 추가할 수 있는 매크로로 구성된 프레임워크입니다.프레임워크에 모든 메소드, 클래스 이름, 기본 클래스 및 필요한 모든 것을 알려야 합니다.

C++는 속도를 염두에 두고 만들어졌습니다.C#이나 Java와 같은 높은 수준의 검사를 원한다면 약간의 노력 없이는 방법이 없다고 말씀드리고 싶습니다.

그리고 나는 조랑말을 좋아하지만 조랑말은 자유롭지 않습니다.:-피

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI 당신이 얻게 될 것입니다.여러분이 생각하는 것과 같은 리플렉션(런타임에 사용할 수 있는 완전한 설명 메타데이터)은 기본적으로 C++에는 존재하지 않습니다.

C++에는 RTTI가 존재하지 않습니다.

이것은 단순히 잘못된 것입니다.실제로 "RTTI"라는 용어는 C++ 표준에서 만들어졌습니다.반면에 RTTI는 리플렉션을 구현하는 데 그리 멀리 가지 않습니다.

정보는 존재하지만 필요한 형식이 아니며 클래스를 내보내는 경우에만 존재합니다.이것은 Windows에서 작동하지만 다른 플랫폼에 대해서는 모르겠습니다.예를 들어 다음과 같이 스토리지 클래스 지정자를 사용합니다.

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

이렇게 하면 컴파일러가 클래스 정의 데이터를 DLL/Exe에 빌드합니다.하지만 쉽게 반영할 수 있는 형식은 아닙니다.

우리 회사에서는 이 메타데이터를 해석하고 추가 매크로 등을 삽입하지 않고도 클래스를 반영할 수 있는 라이브러리를 구축했습니다.수업 자체에.다음과 같이 함수를 호출할 수 있습니다.

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

이는 다음을 효과적으로 수행합니다.

instance_ptr->Foo(1.331);

Invoke(this_pointer,...) 함수에는 가변 인수가 있습니다.분명히 이런 방식으로 함수를 호출하면 const-safety 등과 같은 사항을 피할 수 있으므로 이러한 측면은 런타임 검사로 구현됩니다.

구문이 개선될 수 있다고 확신하며 지금까지는 Win32 및 Win64에서만 작동합니다.우리는 이것이 클래스에 대한 자동 GUI 인터페이스, C++에서 속성 생성, XML과의 스트리밍 등에 매우 유용하며 특정 기본 클래스에서 파생할 필요가 없다는 것을 알았습니다.수요가 충분하다면 출시를 위해 형태를 다듬을 수도 있을 것입니다.

수행하려는 작업이 무엇인지, 그리고 RTTI가 요구 사항을 충족하는지 살펴봐야 합니다.나는 매우 특정한 목적을 위해 내 자신의 의사 반사를 구현했습니다.예를 들어, 나는 시뮬레이션 결과를 유연하게 구성할 수 있기를 원했습니다.출력될 클래스에 일부 상용구 코드를 추가해야 했습니다.

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;
}

첫 번째 호출에서는 이 개체를 필터링 시스템에 추가합니다. BuildMap() 어떤 방법을 사용할 수 있는지 알아보는 방법입니다.

그런 다음 구성 파일에서 다음과 같이 할 수 있습니다.

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

몇 가지 템플릿 마법을 통해 boost, 이는 런타임 시(구성 파일을 읽을 때) 일련의 메서드 호출로 변환되므로 상당히 효율적입니다.꼭 필요한 경우가 아니면 이 작업을 권장하지 않지만, 그렇게 하면 정말 멋진 작업을 수행할 수 있습니다.

반성으로 무엇을 하려고 하시나요?
부스트를 사용할 수 있습니다 유형 특성 그리고 유형 라이브러리는 제한된 형태의 컴파일 타임 리플렉션입니다.즉, 템플릿에 전달된 유형의 기본 속성을 검사하고 수정할 수 있습니다.

나는 사용하는 것이 좋습니다 Qt.

오픈소스 라이선스와 상용 라이선스가 있습니다.

편집하다: 캠프 더 이상 유지되지 않습니다.두 개의 포크를 사용할 수 있습니다:

  • 하나는 라고도 불린다. 캠프 역시 동일한 API를 기반으로 합니다.
  • 숙고하다 부분적으로 재작성되었으며 Boost가 필요하지 않으므로 선호됩니다.C++11을 사용하고 있습니다.

캠프 C++ 언어에 리플렉션을 추가하는 MIT 라이센스 라이브러리(이전 LGPL)입니다.컴파일 시 특정 전처리 단계가 필요하지 않지만 바인딩은 수동으로 수행해야 합니다.

현재 Tegesoft 라이브러리는 Boost를 사용하지만 포크 C++11을 사용하여 더 이상 부스트가 필요하지 않습니다..

나는 당신이 원하는 것과 비슷한 일을 한 적이 있으며, 어느 정도의 반성을 얻고 더 높은 수준의 기능에 액세스하는 것이 가능하지만 유지 관리에 드는 골치 아픈 일은 그만한 가치가 없을 수도 있습니다.내 시스템은 Objective-C의 메시지 전달 및 전달 개념과 유사한 위임을 통해 UI 클래스를 비즈니스 로직과 완전히 분리된 상태로 유지하는 데 사용되었습니다.이를 수행하는 방법은 기호(문자열 풀을 사용했지만 전체 유연성보다 속도 및 컴파일 시간 오류 처리를 선호하는 경우 열거형을 사용하여 수행할 수 있음)를 함수 포인터(실제로는 그렇지 않음)에 매핑할 수 있는 기본 클래스를 만드는 것입니다. 순수 함수 포인터이지만 Boost가 Boost.Function에 있는 것과 유사합니다. 당시에는 액세스할 수 없었습니다.어떤 값이든 표현할 수 있는 공통 기본 클래스가 있는 한 멤버 변수에 대해 동일한 작업을 수행할 수 있습니다.전체 시스템은 키-값 코딩 및 위임을 노골적으로 모방한 것이었고, 시스템을 사용하는 모든 클래스가 해당 메서드와 멤버를 모두 적법한 호출과 일치시키는 데 필요한 엄청난 시간의 가치가 있는 몇 가지 부작용이 있었습니다. :1) 모든 클래스는 헤더를 포함하거나 가짜 기본 클래스를 작성하지 않고도 다른 클래스의 메서드를 호출할 수 있으므로 인터페이스가 컴파일러에 대해 미리 정의될 수 있습니다.2) 멤버 변수의 getter 및 setter는 값을 변경하거나 액세스하는 것이 항상 모든 객체의 기본 클래스에 있는 2개의 메서드를 통해 수행되었기 때문에 스레드로부터 안전하게 만들기가 쉬웠습니다.

또한 C++에서는 쉽지 않은 정말 이상한 일을 할 가능성도 생겼습니다.예를 들어, 자신을 포함하여 모든 유형의 임의 항목을 포함하는 Array 객체를 만들고, 모든 배열 항목에 메시지를 전달하고 반환 값을 수집하여 동적으로 새 배열을 만들 수 있습니다(Lisp의 매핑과 유사).또 다른 하나는 키-값 관찰 구현으로, 지속적으로 데이터를 폴링하거나 불필요하게 디스플레이를 다시 그리는 대신 백엔드 클래스 멤버의 변경 사항에 즉시 응답하도록 UI를 설정할 수 있었습니다.

아마도 더 흥미로운 점은 클래스에 대해 정의된 모든 메서드와 멤버를 문자열 형식으로 덤프할 수도 있다는 사실입니다.

귀찮게 하는 것을 방해할 수 있는 시스템의 단점:모든 메시지와 키-값을 추가하는 것은 매우 지루합니다.반사가 없는 것보다 느립니다.너는 점점 보는 걸 싫어하게 될 거야 boost::static_pointer_cast 그리고 boost::dynamic_pointer_cast 격렬한 열정으로 코드베이스 전체를 뒤덮습니다.강력한 형식의 시스템에는 여전히 한계가 있습니다. 실제로는 이를 약간 숨기는 것이므로 명확하지 않습니다.문자열의 오타도 재미없거나 놀라움을 발견하기 쉽지 않습니다.

다음과 같은 것을 구현하는 방법은 다음과 같습니다.일부 공통 기반에 대한 공유 및 약한 포인터를 사용하고(내 것은 매우 상상적으로 "객체"라고 불림) 사용하려는 모든 유형에 대해 파생됩니다.함수 포인터 호출을 래핑하기 위해 사용자 정의 쓰레기와 수많은 추악한 매크로를 사용하는 방식 대신 Boost.Function을 설치하는 것이 좋습니다.모든 것이 매핑되어 있으므로 객체 검사는 모든 키를 반복하는 문제일 뿐입니다.내 수업은 기본적으로 C++만 사용하여 가능한 한 Cocoa를 직접 복사한 것과 비슷했기 때문에 그런 것을 원한다면 Cocoa 문서를 청사진으로 사용하는 것이 좋습니다.

C++ 시절에 내가 알고 있는 두 가지 반사와 유사한 솔루션은 다음과 같습니다.

1) 모든 클래스가 '객체' 기본 클래스에서 파생되도록 할 수 있는 경우 리플렉션과 유사한 동작을 구축할 수 있는 부트스트랩을 제공하는 RTTI를 사용하십시오.해당 클래스는 GetMethod, GetBaseClass 등과 같은 일부 메서드를 제공할 수 있습니다.이러한 메서드가 작동하는 방식에 관해서는 유형을 장식하기 위해 일부 매크로를 수동으로 추가해야 합니다. 이 매크로는 뒤에서 유형에 메타데이터를 생성하여 GetMethods 등에 대한 답변을 제공합니다.

2) 또 다른 옵션은 컴파일러 개체에 액세스할 수 있는 경우 다음을 사용하는 것입니다. 다이아 SDK.내 기억이 정확하다면 이를 통해 C++ 유형에 대한 메타데이터가 포함된 pdbs를 열 수 있습니다.필요한 일을 하면 충분할 수도 있습니다. 이 페이지 예를 들어 클래스의 모든 기본 유형을 얻는 방법을 보여줍니다.

이 두 솔루션 모두 약간 추악합니다!C#의 고급스러움을 감상하는 데 C++만큼 좋은 것은 없습니다.

행운을 빌어요.

C++에는 리플렉션을 위한 또 다른 새로운 라이브러리가 있습니다. RTTR (런타임 유형 반사, 다음 항목도 참조하세요. 깃허브).

인터페이스는 C#의 리플렉션과 유사하며 RTTI 없이 작동합니다.

편집하다:2017년 2월 7일자로 깨진 링크를 업데이트했습니다.

나는 아무도 이것을 언급하지 않았다고 생각합니다.

CERN에서는 C++에 대한 전체 반사 시스템을 사용합니다.

CERN 반사.아주 잘 작동하는 것 같습니다.

C++에서는 기본적으로 리플렉션이 지원되지 않습니다.이는 방어 테스트를 어렵게 만들기 때문에 슬픈 일입니다.

리플렉션을 수행하는 방법에는 여러 가지가 있습니다.

  1. 디버그 정보를 사용합니다(이식 가능하지 않음).
  2. 매크로/템플릿 또는 기타 소스 접근 방식을 코드에 뿌리세요(보기 흉함)
  3. clang/gcc와 같은 컴파일러를 수정하여 데이터베이스를 생성합니다.
  4. Qt moc 접근 방식 사용
  5. 부스트 리플렉트
  6. 정확하고 평평한 반사

첫 번째 링크는 가장 유망해 보입니다(mod를 사용하여 clang). 두 번째 링크는 여러 기술에 대해 설명하고, 세 번째 링크는 gcc를 사용하는 다른 접근 방식입니다.

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

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

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

이제 C++ 반영을 위한 작업 그룹이 있습니다.C++14 @ CERN에 대한 뉴스를 확인하세요:

17/13/08 편집:원래 게시물 이후로 리플렉션에 대한 많은 잠재적인 발전이 있었습니다.다음은 다양한 기술과 상태에 대한 자세한 내용과 논의를 제공합니다.

  1. 간단히 말해서 정적 반사
  2. 정적 반사
  3. 정적 반사를 위한 디자인

그러나 C++의 리플렉션 지원에 대한 커뮤니티의 관심이 더 커지지 않는 한 가까운 미래에 C++의 표준화된 리플렉션 접근 방식은 유망해 보이지 않습니다.

다음은 지난 C++ 표준 회의의 피드백을 바탕으로 현재 상태를 자세히 설명합니다.

2017년 13월 12일 수정

리플렉션은 C++ 20 이상으로 이동하는 것으로 보이며 아마도 TSR일 것입니다.그러나 움직임은 느립니다.

2018년 9월 15일 수정

TS 초안이 투표를 위해 국가 기관에 발송되었습니다.

텍스트는 여기에서 찾을 수 있습니다: https://github.com/cplusplus/reflection-ts

이 질문은 지금은 조금 오래된 질문이지만(오늘 왜 계속 오래된 질문을 치는지 모르겠습니다) BOOST_FUSION_ADAPT_STRUCT 컴파일 타임 리플렉션을 도입합니다.

물론 이를 런타임 반영에 매핑하는 것은 사용자의 몫이며, 너무 쉽지는 않지만 이 방향으로는 가능하지만 반대 방향으로는 가능하지 않습니다. :)

나는 정말로 매크로가 BOOST_FUSION_ADAPT_STRUCT 런타임 동작을 얻는 데 필요한 메서드를 생성할 수 있습니다.

Dominic Filion이 쓴 "C++에서 리플렉션을 위한 템플릿 사용"이라는 기사가 흥미로울 것 같습니다.1.4절에 있습니다. 게임 프로그래밍 보석 5.불행히도 제 사본은 없지만 귀하가 요구하는 내용을 설명하는 것 같으니 찾아보십시오.

리플렉션은 본질적으로 런타임 코드가 쿼리할 수 있는 코드에 발자국으로 남기기로 결정한 컴파일러에 관한 것입니다.C++는 사용하지 않는 것에 대해서는 비용을 지불하지 않는 것으로 유명합니다.대부분의 사람들은 리플렉션을 사용하지 않거나 원하지 않기 때문에 C++ 컴파일러는 기록하지 않음으로써 비용을 피합니다. 아무것.

따라서 C++은 리플렉션을 제공하지 않으며 다른 답변에서 언급했듯이 일반적인 규칙으로 직접 "시뮬레이트"하는 것은 쉽지 않습니다.

"다른 기술"에서 반성을 갖춘 언어가 없으면 컴파일 타임에 원하는 정보를 추출할 수 있는 도구를 얻으세요.

우리의 DMS 소프트웨어 리엔지니어링 툴킷 명시적인 언어 정의에 의해 매개변수화된 일반화된 컴파일러 기술입니다.C, C++, Java, COBOL, PHP 등에 대한 언어 정의가 있습니다.

C, C++, Java 및 COBOL 버전의 경우 구문 분석 트리 및 기호 테이블 정보에 대한 완전한 액세스를 제공합니다.해당 기호 테이블 정보에는 "반사"에서 원하는 종류의 데이터가 포함됩니다.목표가 일부 필드 또는 메소드 세트를 열거하고 하다 뭔가가 있으면 DMS를 사용하여 기호 테이블에서 찾은 내용에 따라 임의의 방식으로 코드를 변환할 수 있습니다.

여기에서 다른 라이브러리를 찾을 수 있습니다. http://www.garret.ru/cppreflection/docs/reflect.html2가지 방법을 지원합니다:디버그 정보에서 유형 정보를 얻고 프로그래머가 이 정보를 제공하도록 합니다.

나는 또한 내 프로젝트에 대한 반영에 관심이 있었고 이 라이브러리를 찾았습니다. 아직 시도하지는 않았지만 이 사람의 다른 도구를 사용해 보았는데 작동 방식이 마음에 듭니다. :-)

Classdesc를 확인해보세요 http://classdesc.sf.net.클래스 "설명자" 형태로 리플렉션을 제공하고 모든 표준 C++ 컴파일러에서 작동하며(예, Visual Studio 및 GCC에서도 작동하는 것으로 알려져 있습니다) 소스 코드 주석이 필요하지 않습니다(까다로운 상황을 처리하기 위해 일부 pragma가 존재하지만) ).이는 10년 넘게 개발되어 왔으며 다양한 산업 규모의 프로젝트에 사용되었습니다.

C++에서 반영을 원했을 때 읽었습니다. 이 기사 그리고 내가 거기서 본 것보다 더 나아졌습니다.죄송합니다. 캔은 없습니다.나는 결과를 소유하지 않습니다...하지만 당신은 확실히 내가 가진 것을 얻을 수 있고 거기서부터 갈 수 있습니다.

나는 현재 반영 가능한 유형의 정의를 훨씬 쉽게 만들기 위해 Inherit_linearly를 사용하는 방법을 연구하고 있습니다.사실 꽤 멀리까지 왔지만 아직 갈 길이 멀다.C++0x의 변경 사항은 이 분야에서 많은 도움이 될 가능성이 매우 높습니다.

C++에는 아직 이 기능이 없는 것 같습니다.그리고 C++11 연기된 반성도 ((

일부 매크로를 검색하거나 직접 만들어 보세요.Qt는 또한 반사에 도움을 줄 수 있습니다(사용 가능한 경우).

이 프로젝트를 살펴보세요 http://www.garret.ru/cppreflection/docs/reflect.htmlC++에 반사가 추가되었습니다.나중에 사용할 수 있는 클래스에 메타데이터가 추가되었습니다.

C++에서는 리플렉션이 기본적으로 지원되지 않지만 구현하기가 그리 어렵지는 않습니다.나는 다음과 같은 훌륭한 기사를 접했습니다.http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

이 기사에서는 매우 간단하고 기초적인 반사 시스템을 구현하는 방법을 자세히 설명합니다.가장 건전한 해결책은 아니며 정리해야 할 거친 부분이 남아 있지만 내 필요에는 충분했습니다.

결론 - 리플렉션은 올바르게 수행되면 성과를 거둘 수 있으며 C++에서는 완전히 가능합니다.

자동 성찰/반성 툴킷 "IDK"의 존재를 알리고 싶습니다.Qt와 같은 메타 컴파일러를 사용하고 메타 정보를 객체 파일에 직접 추가합니다.사용하기 쉽다고 합니다.외부 종속성이 없습니다.std::string을 자동으로 반영한 다음 이를 스크립트에서 사용할 수도 있습니다.좀 봐주세요 IDK

C++의 리플렉션은 각 멤버에 대해 몇 가지 메서드를 실행해야 하는 경우 매우 유용합니다(예:직렬화, 해싱, 비교).나는 매우 간단한 구문을 사용하여 일반적인 솔루션을 제공했습니다.

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

여기서 ENUMERATE_MEMBERS는 나중에 설명되는 매크로입니다(업데이트):

int 및 std::string에 대한 직렬화 함수를 다음과 같이 정의했다고 가정합니다.

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());
}

그리고 "비밀 매크로" 근처에 일반 기능이 있습니다.)

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

이제 당신은 쓸 수 있습니다

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)

따라서 구조체 정의에 ENUMERATE_MEMBERS 매크로가 있으면 원래 유형을 건드리지 않고도 직렬화, 비교, 해싱 및 기타 기능을 구축할 수 있습니다. 유일한 요구 사항은 열거자별로(BinaryWriter와 같이) 열거할 수 없는 각 유형에 대해 "EnumerateWith" 메서드를 구현하는 것입니다. .일반적으로 프로젝트의 모든 유형을 지원하려면 10-20개의 "단순" 유형을 구현해야 합니다.

이 매크로는 런타임에 구조체 생성/파괴에 대한 오버헤드가 없어야 하며 T.EnumerateWith()의 코드는 필요에 따라 생성되어야 하며 이는 템플릿 인라인 함수로 만들어 달성할 수 있으므로 모든 이야기는 각 구조체에 ENUMERATE_MEMBERS(m1,m2,m3...)를 추가하는 것입니다. 반면 멤버 유형별로 특정 메서드를 구현하는 것은 모든 솔루션에서 필수이므로 오버헤드로 가정하지 않습니다.

업데이트:ENUMERATE_MEMBERS 매크로의 매우 간단한 구현이 있습니다(그러나 열거 가능한 구조체에서 상속을 지원하도록 약간 확장될 수 있음).

#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);
}

그리고 이 15줄의 코드에는 타사 라이브러리가 필요하지 않습니다.

C++20을 사용하면 다음과 같은 이점을 얻을 수 있습니다. 확장문, 집계 유형을 반복할 수 있습니다.

struct my_type {
    double data;
    std::string another_data;
    int last_data;
};

auto object = my_type{};

for...(auto& member : object) {
    using member_type = std::remove_cvref_t<decltype(member)>;
    member = get_data<member_type>();
}

상대적으로 간단한 C++ 반영을 찾고 있다면 다양한 소스 매크로/정의를 수집하고 작동 방식에 대해 주석을 달았습니다.여기에서 헤더 파일을 다운로드 할 수 있습니다.

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

정의 세트와 그 위에 기능:

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

샘플 애플리케이션은 git 저장소에도 있습니다.https://github.com/tapika/TestCppReflect/

설명과 함께 여기에 부분적으로 복사하겠습니다.

#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 정의는 클래스 이름 + 필드 이름을 사용합니다. offsetof - 특정 필드가 메모리의 어느 위치에 있는지 식별합니다..NET 용어를 최대한 익히려고 노력했지만 C++과 C#은 다르기 때문에 1:1이 아닙니다.전체 C++ 반사 모델은 다음 위치에 있습니다. TypeInfo 그리고 FieldInfo 클래스.

나는 pugi xml 파서를 사용하여 데모 코드를 xml로 가져오고 xml에서 다시 복원했습니다.

따라서 데모 코드로 생성된 출력은 다음과 같습니다.

<?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>

TypeTraits 클래스 및 부분 템플릿 사양을 통해 타사 클래스/구조 지원을 활성화할 수도 있습니다. CString 또는 int와 유사한 방식으로 자체 TypeTraitsT 클래스를 정의할 수 있습니다. 예제 코드는 다음에서 참조하세요.

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

이 솔루션은 Windows/Visual Studio에 적용 가능합니다.다른 OS/컴파일러로 포팅하는 것이 가능하지만 아직 그렇게 하지 않았습니다.(솔루션이 정말 마음에 드는지 물어보세요. 제가 도와드릴 수도 있습니다.)

이 솔루션은 여러 하위 클래스가 있는 한 클래스의 일회성 직렬화에 적용 가능합니다.

그러나 클래스 부분을 직렬화하거나 리플렉션 호출이 생성하는 기능을 제어하는 ​​메커니즘을 찾고 있다면 다음 솔루션을 살펴볼 수 있습니다.

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

더 자세한 정보는 유튜브 영상에서 보실 수 있습니다:

C++ 런타임 유형 반사https://youtu.be/TN8tJijkeFE

나는 C++ 리플렉션이 어떻게 작동하는지 좀 더 자세히 설명하려고 합니다.

샘플 코드는 예를 들어 다음과 같습니다.

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;

그러나 여기서 각 단계는 실제로 C ++ 속성을 사용하여 기능 호출을 초래합니다. __declspec(property(get =, put ... ).

이는 C++ 데이터 유형, C++ 속성 이름 및 클래스 인스턴스 포인터에 대한 전체 정보를 경로 형식으로 수신하고 해당 정보를 기반으로 xml, json을 생성하거나 인터넷을 통해 해당 정보를 직렬화할 수도 있습니다.

이러한 가상 콜백 함수의 예는 다음에서 찾을 수 있습니다.

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

기능 보기 ReflectCopy, 및 가상 기능 ::OnAfterSetProperty.

하지만 매우 고급스러운 주제이므로 먼저 영상을 통해 확인해 보시기를 권합니다.

개선 아이디어가 있으면 언제든지 저에게 연락해 주세요.

Root Reflex 프로젝트는 이를 지원합니다.

보다 https://root.cern.ch/how/how-use-reflex

다음과 같이 함수에 대한 포인터를 선언하는 경우:

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

다음과 같이 해당 기능에 메모리 위치를 할당할 수 있습니다. libdl 그리고 dlopen)

#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);
}

간접 참조를 사용하여 지역 기호를 로드하려면 다음을 사용할 수 있습니다. dlopen 호출 바이너리(argv[0]).

이에 대한 유일한 요구 사항은 다음과 같습니다. dlopen(), libdl, 그리고 dlfcn.h)는 함수의 인수와 유형을 알고 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top