x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Office Holiday

Epic Games' offices will be on holiday from June 22nd to July 7th. During this period support will be limited. Our offices will reopen on Monday, July 8th. 

I am trying to use boost, C++ with templates, and factory pattern but can't seem to figure out the right syntax for it?

Although this is a C++ question, I am trying to use this with unreal engine ot create my own classes. Here is the specifics:

  1. Consider a simple constructor in a class with two input arguments, concreteclass(_1, _2).

  2. I have a map for this instantiation, map ``.

  3. Also, these classes work with different datatypes concreteclass(_1,_2) is different from concreteclass(_1,_2).

Now that my problem is described above here is what I try to do using boost::factory pattern, classes defined in a string map and datatypes defined in an enum.

First, there is a simple way to demonstrate how boost factory pattern can be used with constructor arguments, the following code nicely works:

 // Factory which takes two arguments
 struct base {
     base(int alpha) : alpha(alpha) {}
     virtual ~base() = default;
     virtual void print() const = 0;
 
     int alpha;
 };
 
 struct derived : public base {
     derived(int alpha, int beta) : base(alpha), beta(beta) {}
     void print() const override {
         std::cout << alpha << " " << beta << std::endl;
     }
 
     int beta;
 };
 
 
 void TestBoostFactoryWithTwoArgs()
 {
     // Constructor factory with two input args
     {
         std::map<std::string, boost::function<base* (int&, int&)>> factories;
         factories["derived"] = boost::bind(boost::factory<derived*>(), _1, _2);
         int x = 42;
         int y = 51;
         std::unique_ptr<base> b{ factories.at("derived")(x,y) };
         b->print();
     }
     // Factory with two initialized inputs args - binding of values not at run time 
     {
         std::map<std::string, boost::function<base* ()>> factories;
         factories["derived"] = boost::bind(boost::factory<derived*>(), 42, 51);
         std::unique_ptr<base> b{ factories.at("derived")() };
         b->print();
     }
 }

Now consider my code - SimpleClasses.h:

 // Dummy base class - non template
 class IBaseClass
 {
 public:
 
 };
 
 // Templatized Derived Base class
 template <typename T>
 class ConcreteClass : public IBaseClass
 {
 private:
     std::shared_ptr<IBaseClass> m_leftArgument;
     std::shared_ptr<IBaseClass> m_leftArgument;
 public:
     ConcreteClass(std::unique_ptr<IBaseClass>& leftArgument, std::unique_ptr<IBaseClass>& rightArgument)
     {
         m_leftArgument = leftArgument;
         m_rightArgument = rightArgument;
     };
 
     virtual T DoSomething()
     {
         cout << "I did something in Concrete Base Class" << endl;
         return T();
     }; // This is the main reason for creating T
 
 };
 
 template <typename T>
 class ConcreteClassA : ConcreteClass
 {
 
 };
 
 template <typename T>
 class ConcreteClassB : ConcreteClass
 {
 
 };
 
 template <typename T>
 class ConcreteClassC : ConcreteClass
 {
 
 };

Another File, ClassFactory.h :

 #pragma once
 
 #include "SimpleClasses.h"
 #include <memory> 
 #include <map>
 #include <boost/functional/overloaded_function.hpp>
 #include <boost/functional/factory.hpp>
 
 using namespace std;
 
 // Add More class Keys here
 namespace MyClassesNamespace { // These are all string keys
     static const string CLASS_A = "specialclassA";
     static const string CLASS_B = "specialclassB";
     static const string CLASS_C = "specialclassC";
 };
 
 enum EMyDataTypes
 {
     INT8,
     FLOAT8,
     FLOAT16,
 };
 
 
 // This type def we keep for non templatized base class constructor
 typedef boost::function<IBaseClass*(std::unique_ptr<IBaseClass>&, std::unique_ptr<IBaseClass>&)> IBaseClassConstructorFunc_factory;
 
 // Dummy base factory - no template  
 class UBaseClassTemplateFactory
 {
 public:
 };
 
 template<typename T>
 class UClassFactoryTemplate : public UBaseClassTemplateFactory
 {
 private:
     static std::map<string, IBaseClassConstructorFunc_factory> ClassFactoryTemplateMap; // Unique Classes only
 
 
 public:
     UClassFactoryTemplate();
     __forceinline static UClassFactoryTemplate*Get()
     {
         static UClassFactoryTemplate<T> SingletonInstance;
         return &SingletonInstance;
     }
 
     static std::unique_ptr<IBaseClass<T>> CreateClassTemplatized(string ClassString, std::unique_ptr<IBaseClass> LeftArgument, std::unique_ptr<IBaseClass> RightArgument);
 };
 
 
 // This type def we keep for non templatized base class
 typedef boost::function<UBaseClassTemplateFactory*()> ClassFactoryTemplate_factory;
 
 
 /* This is the instance class that resolves the classes as well as the concrete datatype to be used in UClassFactoryTemplate*/
 class UClassFactory
 {
 
 private:
     UClassFactory();
     static std::map<EMyDataTypes, ClassFactoryTemplate_factory> ClassDataTypeTemplateFactoryMap;
     
 
 public:
 
     __forceinline static UClassFactory *Get()
     {
         static UClassFactory SingletonInstance;
         return &SingletonInstance;
     }
 
     static std::unique_ptr<IBaseClass> CreateConcreteClass(string ClassString, std::unique_ptr<IBaseClass> LeftVal, std::unique_ptr<IBaseClass> RightVal, EMyDataTypes someEnumVal = EMyDataTypes::INT8);
 
 };

Finally, in ClassFactory.cpp

 #include "ClassFactory.h"
 #include <boost/bind.hpp>
 
 /*static, but non-const data members should be defined outside of the class definition
 *and inside the namespace enclosing the class. The usual practice is to define it in
 *the translation unit (*.cpp) because it is considered to be an implementation detail.
 *Only static and const integral types can be declared and defined at the same time (inside class definition):*/
 
 template<typename T>
 std::map<string, IBaseClassConstructorFunc_factory> UClassFactoryTemplate<T>::ClassFactoryTemplateMap;
 std::map<EMyDataTypes, ClassFactoryTemplate_factory> UClassFactory::ClassDataTypeTemplateFactoryMap;
 
 
 template<typename T>
 inline UClassFactoryTemplate<T>::UClassFactoryTemplate()
 {
     ClassFactoryTemplateMap[MyClassesNamespace::CLASS_A] = boost::bind(boost::factory<ConcreteClassA<T>*>(), _1, _2);
     ClassFactoryTemplateMap[MyClassesNamespace::CLASS_B] = boost::bind(boost::factory<ConcreteClassB<T>*>(), _1, _2);
     ClassFactoryTemplateMap[MyClassesNamespace::CLASS_C] = boost::bind(boost::factory<ConcreteClassC<T>*>(), _1, _2);
 }
 
 template<typename T>
 std::unique_ptr<IBaseClass<T>> UClassFactoryTemplate<T>::CreateClassTemplatized(string ClassString, std::unique_ptr<IBaseClass> LeftArgument, std::unique_ptr<IBaseClass> RightArgument)
 {
     std::unique_ptr<IBaseClass<T>> someTemplatizedDataTypeInstance{ ClassFactoryTemplateMap.at(ClassString) (LeftArgument,RightArgument) };
     return someTemplatizedDataTypeInstance;
 }
 
 UClassFactory::UClassFactory()
 {
     ClassDataTypeTemplateFactoryMap[EMyDataTypes::INT8] = boost::bind(boost::factory<UClassFactoryTemplate<int>*>());
     ClassDataTypeTemplateFactoryMap[EMyDataTypes::FLOAT8] = boost::bind(boost::factory<UClassFactoryTemplate<float>*>());
     ClassDataTypeTemplateFactoryMap[EMyDataTypes::FLOAT16] = boost::bind(boost::factory<UClassFactoryTemplate<double>*>());
 }
 
 std::unique_ptr<IBaseClass> UClassFactory::CreateConcreteClass(string ClassString, std::unique_ptr<IBaseClass> LeftVal, std::unique_ptr<IBaseClass> RightVal, EMyDataTypes someEnumVal)
 {
     std::unique_ptr<UBaseClassTemplateFactory>  BaseOperatorTempFactory{ ClassDataTypeTemplateFactoryMap.at(someEnumVal) };
     return BaseOperatorTempFactory->Get()::CreateClassTemplatized(ClassString, LeftVal, RightVal);
 }

 

The question now is, the above code does not even compile let alone run, it says abstract class cannot be instantiated for the templatized map. I just want the UClassFactory to return me correct instantiated class like A,B,C based on a string map with correct datatypes based on an enum. How do I achieve this combination? I wonder what is the correct syntax? Or is my approach inherently flawed? Or there is a nice way to instantiate classes with factory pattern and different datatypes? Please let me know any suggestions/ comments.

Thanks

Alam

Product Version: UE 4.15
Tags:
more ▼

asked May 20 '17 at 08:06 PM in C++ Programming

avatar image

Infiltrator_
13 3 10 12

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

0 answers: sort voted first
Be the first one to answer this question
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question