클래스 이름을 가진 문자열에서 객체를 인스턴스화하는 방법이 있습니까? { if(msClassName

파일이 있습니다 : Base.h

class Base;
class DerivedA : public Base;
class DerivedB : public Base;

/*etc...*/

그리고 다른 파일 : BaseFactory.h

#include "Base.h"

class BaseFactory
{
public:
  BaseFactory(const string &sClassName){msClassName = sClassName;};

  Base * Create()
  {
    if(msClassName == "DerivedA")
    {
      return new DerivedA();
    }
    else if(msClassName == "DerivedB")
    {
      return new DerivedB();
    }
    else if(/*etc...*/)
    {
      /*etc...*/
    }
  };
private:
  string msClassName;
};

/*etc.*/

어떻게 든이 문자열을 실제 유형 (클래스)으로 변환하여 BaseFactory가 가능한 모든 파생 클래스를 알 필요가 없으며 각 클래스에 대해 if ()를 가질 필요가 없습니까? 이 문자열에서 수업을 만들 수 있습니까?

C #에서 Reflection을 통해이 작업을 수행 할 수 있다고 생각합니다. C ++에 비슷한 것이 있습니까?



답변

당신이 직접 매핑을하지 않는 한 아무 것도 없습니다. C ++에는 런타임에 유형이 결정되는 객체를 생성하는 메커니즘이 없습니다. 그래도 맵을 사용하여 직접 맵핑을 수행 할 수 있습니다.

template<typename T> Base * createInstance() { return new T; }

typedef std::map<std::string, Base*(*)()> map_type;

map_type map;
map["DerivedA"] = &createInstance<DerivedA>;
map["DerivedB"] = &createInstance<DerivedB>;

그리고 당신은 할 수 있습니다

return map[some_string]();

새 인스턴스를 가져옵니다. 또 다른 아이디어는 유형을 직접 등록하는 것입니다.

// in base.hpp:
template<typename T> Base * createT() { return new T; }

struct BaseFactory {
    typedef std::map<std::string, Base*(*)()> map_type;

    static Base * createInstance(std::string const& s) {
        map_type::iterator it = getMap()->find(s);
        if(it == getMap()->end())
            return 0;
        return it->second();
    }

protected:
    static map_type * getMap() {
        // never delete'ed. (exist until program termination)
        // because we can't guarantee correct destruction order 
        if(!map) { map = new map_type; }
        return map;
    }

private:
    static map_type * map;
};

template<typename T>
struct DerivedRegister : BaseFactory {
    DerivedRegister(std::string const& s) {
        getMap()->insert(std::make_pair(s, &createT<T>));
    }
};

// in derivedb.hpp
class DerivedB {
    ...;
private:
    static DerivedRegister<DerivedB> reg;
};

// in derivedb.cpp:
DerivedRegister<DerivedB> DerivedB::reg("DerivedB");

등록 용 매크로를 만들 수 있습니다

#define REGISTER_DEC_TYPE(NAME) \
    static DerivedRegister<NAME> reg

#define REGISTER_DEF_TYPE(NAME) \
    DerivedRegister<NAME> NAME::reg(#NAME)

그래도 그 두 가지 이름이 더 좋을 것입니다. 아마 여기서 사용하는 것이 합리적입니다 shared_ptr.

공통 기본 클래스가없는 관련되지 않은 유형 세트가있는 경우 boost::variant<A, B, C, D, ...>대신 함수 포인터에 리턴 유형을 제공 할 수 있습니다 . Foo, Bar 및 Baz 클래스가 있다면 다음과 같습니다.

typedef boost::variant<Foo, Bar, Baz> variant_type;
template<typename T> variant_type createInstance() {
    return variant_type(T());
}

typedef std::map<std::string, variant_type (*)()> map_type;

A boost::variant는 노조와 같습니다. 초기화 또는 할당에 사용 된 객체를 확인하여 어떤 유형이 저장되어 있는지 파악합니다. 여기 에서 설명서를 살펴보십시오 . 마지막으로, 원시 함수 포인터를 사용하는 것도 약간 오래되었습니다. 최신 C ++ 코드는 특정 기능 / 유형에서 분리되어야합니다. Boost.Function더 나은 방법을 찾기 위해 조사 할 수도 있습니다 . 다음과 같이 보입니다 (지도).

typedef std::map<std::string, boost::function<variant_type()> > map_type;

std::function를 포함하여 다음 버전의 C ++에서도 사용할 수 있습니다 std::shared_ptr.


답변

없습니다. 이 문제에 대한 내가 선호하는 해결책은 이름을 생성 방법에 매핑하는 사전을 만드는 것입니다. 이와 같이 생성하려는 클래스는 사전에 생성 방법을 등록합니다. 이에 대해서는 GoF 패턴 책 에서 자세히 설명합니다 .


답변

짧은 대답은 당신이 할 수 없다는 것입니다. 이유는 다음 SO 질문을 참조하십시오.

  1. C ++에 리플렉션이없는 이유는 무엇입니까?
  2. C ++ 애플리케이션에 리플렉션을 추가하려면 어떻게해야합니까?

답변

C ++ 팩토리에 대한 또 다른 SO 질문에 대답했습니다. 유연한 공장에 관심 있다면 여기를 참조하십시오 . ET ++에서 오래된 매크로를 사용하여 나에게 큰 도움이되는 오래된 방법을 설명하려고합니다.

ET ++ 는 오래된 MacApp을 C ++ 및 X11로 이식하는 프로젝트였습니다. 이를 위해 Eric Gamma 등은 디자인 패턴에 대해 생각하기 시작했습니다.


답변

boost :: functional에는 매우 유연한 팩토리 템플릿이 있습니다. http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html

그러나 선호하는 것은 매핑 및 객체 생성 메커니즘을 숨기는 래퍼 클래스를 생성하는 것입니다. 내가 겪는 일반적인 시나리오는 기본 클래스의 여러 파생 클래스를 키에 매핑해야하며, 파생 클래스에는 모두 공통 생성자 서명이 있습니다. 지금까지 내가 생각해 낸 해결책은 다음과 같습니다.

#ifndef GENERIC_FACTORY_HPP_INCLUDED

//BOOST_PP_IS_ITERATING is defined when we are iterating over this header file.
#ifndef BOOST_PP_IS_ITERATING

    //Included headers.
    #include <unordered_map>
    #include <functional>
    #include <boost/preprocessor/iteration/iterate.hpp>
    #include <boost/preprocessor/repetition.hpp>

    //The GENERIC_FACTORY_MAX_ARITY directive controls the number of factory classes which will be generated.
    #ifndef GENERIC_FACTORY_MAX_ARITY
        #define GENERIC_FACTORY_MAX_ARITY 10
    #endif

    //This macro magic generates GENERIC_FACTORY_MAX_ARITY + 1 versions of the GenericFactory class.
    //Each class generated will have a suffix of the number of parameters taken by the derived type constructors.
    #define BOOST_PP_FILENAME_1 "GenericFactory.hpp"
    #define BOOST_PP_ITERATION_LIMITS (0,GENERIC_FACTORY_MAX_ARITY)
    #include BOOST_PP_ITERATE()

    #define GENERIC_FACTORY_HPP_INCLUDED

#else

    #define N BOOST_PP_ITERATION() //This is the Nth iteration of the header file.
    #define GENERIC_FACTORY_APPEND_PLACEHOLDER(z, current, last) BOOST_PP_COMMA() BOOST_PP_CAT(std::placeholders::_, BOOST_PP_ADD(current, 1))

    //This is the class which we are generating multiple times
    template <class KeyType, class BasePointerType BOOST_PP_ENUM_TRAILING_PARAMS(N, typename T)>
    class BOOST_PP_CAT(GenericFactory_, N)
    {
        public:
            typedef BasePointerType result_type;

        public:
            virtual ~BOOST_PP_CAT(GenericFactory_, N)() {}

            //Registers a derived type against a particular key.
            template <class DerivedType>
            void Register(const KeyType& key)
            {
                m_creatorMap[key] = std::bind(&BOOST_PP_CAT(GenericFactory_, N)::CreateImpl<DerivedType>, this BOOST_PP_REPEAT(N, GENERIC_FACTORY_APPEND_PLACEHOLDER, N));
            }

            //Deregisters an existing registration.
            bool Deregister(const KeyType& key)
            {
                return (m_creatorMap.erase(key) == 1);
            }

            //Returns true if the key is registered in this factory, false otherwise.
            bool IsCreatable(const KeyType& key) const
            {
                return (m_creatorMap.count(key) != 0);
            }

            //Creates the derived type associated with key. Throws std::out_of_range if key not found.
            BasePointerType Create(const KeyType& key BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(N,const T,& a)) const
            {
                return m_creatorMap.at(key)(BOOST_PP_ENUM_PARAMS(N,a));
            }

        private:
            //This method performs the creation of the derived type object on the heap.
            template <class DerivedType>
            BasePointerType CreateImpl(BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& a))
            {
                BasePointerType pNewObject(new DerivedType(BOOST_PP_ENUM_PARAMS(N,a)));
                return pNewObject;
            }

        private:
            typedef std::function<BasePointerType (BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& BOOST_PP_INTERCEPT))> CreatorFuncType;
            typedef std::unordered_map<KeyType, CreatorFuncType> CreatorMapType;
            CreatorMapType m_creatorMap;
    };

    #undef N
    #undef GENERIC_FACTORY_APPEND_PLACEHOLDER

#endif // defined(BOOST_PP_IS_ITERATING)
#endif // include guard

나는 일반적으로 거시적 인 매크로 사용에 반대하지만 여기서 예외를 만들었습니다. 위 코드는 0에서 GENERIC_FACTORY_MAX_ARITY (포함) 사이의 각 N에 대해 GenericFactory_N이라는 클래스의 GENERIC_FACTORY_MAX_ARITY + 1 버전의 클래스를 생성합니다.

생성 된 클래스 템플릿을 사용하는 것은 쉽습니다. 팩토리가 문자열 맵핑을 사용하여 BaseClass 파생 오브젝트를 작성하려고한다고 가정하십시오. 파생 된 각 객체는 생성자 매개 변수로 3 개의 정수를 사용합니다.

#include "GenericFactory.hpp"

typedef GenericFactory_3<std::string, std::shared_ptr<BaseClass>, int, int int> factory_type;

factory_type factory;
factory.Register<DerivedClass1>("DerivedType1");
factory.Register<DerivedClass2>("DerivedType2");
factory.Register<DerivedClass3>("DerivedType3");

factory_type::result_type someNewObject1 = factory.Create("DerivedType2", 1, 2, 3);
factory_type::result_type someNewObject2 = factory.Create("DerivedType1", 4, 5, 6);

GenericFactory_N 클래스 소멸자는 다음을 허용하기 위해 가상입니다.

class SomeBaseFactory : public GenericFactory_2<int, BaseType*, std::string, bool>
{
    public:
        SomeBaseFactory() : GenericFactory_2()
        {
            Register<SomeDerived1>(1);
            Register<SomeDerived2>(2);
        }
};

SomeBaseFactory factory;
SomeBaseFactory::result_type someObject = factory.Create(1, "Hi", true);
delete someObject;

일반 팩토리 생성기 매크로의이 줄은

#define BOOST_PP_FILENAME_1 "GenericFactory.hpp"

일반 팩토리 헤더 파일의 이름이 GenericFactory.hpp라고 가정합니다.


답변

객체를 등록하고 문자열 이름으로 액세스하기위한 상세 솔루션.

common.h:

#ifndef COMMON_H_
#define COMMON_H_


#include<iostream>
#include<string>
#include<iomanip>
#include<map>

using namespace std;
class Base{
public:
    Base(){cout <<"Base constructor\n";}
    virtual ~Base(){cout <<"Base destructor\n";}
};
#endif /* COMMON_H_ */

test1.h:

/*
 * test1.h
 *
 *  Created on: 28-Dec-2015
 *      Author: ravi.prasad
 */

#ifndef TEST1_H_
#define TEST1_H_
#include "common.h"

class test1: public Base{
    int m_a;
    int m_b;
public:
    test1(int a=0, int b=0):m_a(a),m_b(b)
    {
        cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl;
    }
    virtual ~test1(){cout <<"test1 destructor\n";}
};



#endif /* TEST1_H_ */

3. test2.h
#ifndef TEST2_H_
#define TEST2_H_
#include "common.h"

class test2: public Base{
    int m_a;
    int m_b;
public:
    test2(int a=0, int b=0):m_a(a),m_b(b)
    {
        cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl;
    }
    virtual ~test2(){cout <<"test2 destructor\n";}
};


#endif /* TEST2_H_ */

main.cpp:

#include "test1.h"
#include "test2.h"

template<typename T> Base * createInstance(int a, int b) { return new T(a,b); }

typedef std::map<std::string, Base* (*)(int,int)> map_type;

map_type mymap;

int main()
{

    mymap["test1"] = &createInstance<test1>;
    mymap["test2"] = &createInstance<test2>;

     /*for (map_type::iterator it=mymap.begin(); it!=mymap.end(); ++it)
        std::cout << it->first << " => " << it->second(10,20) << '\n';*/

    Base *b = mymap["test1"](10,20);
    Base *b2 = mymap["test2"](30,40);

    return 0;
}

컴파일하고 실행하십시오 (Eclipse 로이 작업을 수행 했음)

산출:

Base constructor
test1 constructor m_a=10m_b=20
Base constructor
test1 constructor m_a=30m_b=40


답변

Java에서와 같이 의미를 반영합니다. 여기에 몇 가지 정보가 있습니다 :
http://msdn.microsoft.com/en-us/library/y0114hz2(VS.80).aspx

일반적으로 Google에서 “C ++ 리플렉션”을 검색하십시오.