Asked  6 Months ago    Answers:  5   Viewed   30 times

I have a file: Base.h

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

/*etc...*/

and another file: 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.*/

Is there a way to somehow convert this string to an actual type (class), so that BaseFactory wouldn't have to know all the possible Derived classes, and have if() for each one of them? Can I produce a class from this string?

I think this can be done in C# through Reflection. Is there something similar in C++?

 Answers

46

Nope, there is none, unless you do the mapping yourself. C++ has no mechanism to create objects whose types are determined at runtime. You can use a map to do that mapping yourself, though:

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

And then you can do

return map[some_string]();

Getting a new instance. Another idea is to have the types register themself:

// 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");

You could decide to create a macro for the registration

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

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

I'm sure there are better names for those two though. Another thing which probably makes sense to use here is shared_ptr.

If you have a set of unrelated types that have no common base-class, you can give the function pointer a return type of boost::variant<A, B, C, D, ...> instead. Like if you have a class Foo, Bar and Baz, it looks like this:

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 is like an union. It knows which type is stored in it by looking what object was used for initializing or assigning to it. Have a look at its documentation here. Finally, the use of a raw function pointer is also a bit oldish. Modern C++ code should be decoupled from specific functions / types. You may want to look into Boost.Function to look for a better way. It would look like this then (the map):

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

std::function will be available in the next version of C++ too, including std::shared_ptr.

Tuesday, June 1, 2021
 
christina
answered 6 Months ago
27

Two ways:

Method 1 - only for classes having a no-arg constructor

If your class has a no-arg constructor, you can get a Class object using Class.forName() and use the newInstance() method to create an instance (though beware that this method is often considered evil because it can defeat Java's checked exceptions).

For example:

Class<?> clazz = Class.forName("java.util.Date");
Object date = clazz.newInstance();

Method 2

An alternative safer approach which also works if the class doesn't have any no-arg constructors is to query your class object to get its Constructor object and call a newInstance() method on this object:

Class<?> clazz = Class.forName("com.foo.MyClass");
Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
Object instance = constructor.newInstance("stringparam", 42);

Both methods are known as reflection. You will typically have to catch the various exceptions which can occur, including things like:

  • the JVM can't find or can't load your class
  • the class you're trying to instantiate doesn't have the right sort of constructors
  • the constructor itself threw an exception
  • the constructor you're trying to invoke isn't public
  • a security manager has been installed and is preventing reflection from occurring
Tuesday, June 1, 2021
 
Nil
answered 6 Months ago
Nil
19

By implementing the interface explicitly, like this:

public interface ITest {
    void Test();
}
public interface ITest2 {
    void Test();
}
public class Dual : ITest, ITest2
{
    void ITest.Test() {
        Console.WriteLine("ITest.Test");
    }
    void ITest2.Test() {
        Console.WriteLine("ITest2.Test");
    }
}

When using explicit interface implementations, the functions are not public on the class. Therefore in order to access these functions, you have to first cast the object to the interface type, or assign it to a variable declared of the interface type.

var dual = new Dual();
// Call the ITest.Test() function by first assigning to an explicitly typed variable
ITest test = dual;
test.Test();
// Call the ITest2.Test() function by using a type cast.
((ITest2)dual).Test();
Tuesday, June 1, 2021
 
Litty
answered 6 Months ago
37

Be warned that using

java -verbose

Will produce an enormous amount of output. Log the output to a file and then use grep. If you have the 'tee' filter you could try this:

java -verbose | tee classloader.log
grep class classloader.log
Tuesday, June 29, 2021
 
Anand
answered 5 Months ago
28

First of all, if someone wants to implement a factory pattern, an acceptable way of doing it with raw pointers is as follows:

#include <iostream>
#include <map>

class A;

class A_Factory {
 public:
  A_Factory() {}
  virtual A *create() = 0;
};

class A {
 public:
  A() {}
  static void registerType(int n, A_Factory *factory) {
    get_factory_instance()[n] = factory;
  }
  static A *create(int n) {
    A *A_instance = get_factory_instance()[n]->create();
    return A_instance;
  }
  virtual void setMyID(int n) {}
  virtual void I_am() { std::cout << "I am An"; }
  virtual ~A() {}

 protected:
  int MyID;
  static std::map<int, A_Factory *> &get_factory_instance() {
    static std::map<int, A_Factory *> map_instance;
    return map_instance;
  }
};

class B : public A {
 public:
  B() {}
  void Foo() {}
  void I_am() { std::cout << "I am B " << MyID << "n"; }
  void setMyID(int n) { MyID = n; }
  ~B() {}

 private:
};

class B_Factory : public A_Factory {
 public:
  B_Factory() { A::registerType(1, this); }
  A *create() { return new B(); }
};

static B_Factory b0_factory;

void caller() {}

int main() {
  A *b1 = A::create(1);
  A *b2 = A::create(1);
  b1->setMyID(10);
  b2->setMyID(20);
  b1->I_am();
  b2->I_am();
  delete b1;
  delete b2;

  return 0;
}

A is the base class, and B is the derived one. If we pass 1 to A::create(int n), an object of type B will be produced. The memory is managed manually and there would be no memory leak.

Concerning the questions in the post:

  1. YES. unique_ptr is awesome; use them wherever you can!
  2. With the design presented in the question, passing the ownership of this was somehow necessary. I cannot think of a way to pass the ownership of this. With the design presented in the answer, it is not necessary to pass the ownership of this.
  3. Implement the unique_ptr in the above factory pattern as below:
#include <iostream>
#include <map>
#include <memory>

 class A;

 class A_Factory {
 public:
  A_Factory() {}
  virtual std::unique_ptr<A> create_unique() = 0;
};

 class A {
 public:
  A() {}
  static void registerType(int n, A_Factory *factory) {
    get_factory_instance()[n] = factory;
  }
  static std::unique_ptr<A> create_unique(int n) {
    std::unique_ptr<A> A_instance =
        std::move(get_factory_instance()[n]->create_unique());
    return A_instance;
  }

  virtual void setMyID(int n) {}
  virtual void I_am() { std::cout << "I am An"; }
  virtual ~A() {}

 protected:
  int MyID;
  static std::map<int, A_Factory *> &get_factory_instance() {
    static std::map<int, A_Factory *> map_instance;
    return map_instance;
  }
};

 class B : public A {
 public:
  B() {}
  void Foo() {}
  void I_am() { std::cout << "I am B " << MyID << "n"; }
  void setMyID(int n) { MyID = n; }
  ~B() {}

 private:
};

 class B_Factory : public A_Factory {
 public:
  B_Factory() { A::registerType(1, this); }
  std::unique_ptr<A> create_unique() {
    std::unique_ptr<A> ptr_to_B(new B);
    return ptr_to_B;
  }
};

 static B_Factory b0_factory;

 void caller() {}

 int main() {
  std::unique_ptr<A> b1 = std::move(A::create_unique(1));
  std::unique_ptr<A> b2 = std::move(A::create_unique(1));
  b1->setMyID(10);
  b2->setMyID(20);
  b1->I_am();
  b2->I_am();

  return 0;
}

As you can see, no manual memory management is necessary and the memory management is handled by the unique_ptr.

Thursday, September 23, 2021
 
MGP
answered 2 Months ago
MGP
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share