2

I want to know whether it is possible or not.

I have a class in one project.

    class A { ...
    void CreateInternal(void);
    B* m_pData;
    ... }

void A::CreateInternal(void)
{
  m_pData= new B;
}

and in other project, i add C class and want to make it in A::CreateInernal(void) like this...

void A::CreateInternal(void)
{
  m_pData = new C; // ex) B is base class of C
}

Of course, i can make Class A to template class, however, i do not want it. I want to use it last. ( Suppose that dependency or other build error is free )

    typename<class T>
    class A { ...
    void CreateInternal(void);
    T* m_pData
        ... }

    void A<T>::CreateInternal(void)
    {
       m_pData = new T;
    }

I just want to register Class B or Class C in Class A. Is it possible??

Thank you for your reply!

이준학
  • 31
  • 4
  • 2
    You can erase the type and store `void*` but then you need to preserve the type information elsewhere. If `B` and `C` have a common base class, you can also store a pointer to that base. – Henri Menke Aug 22 '18 at 05:03
  • Alternatively you could pass in a flag to CreateInternal to specify whether you are creating B or C. Though for whatever reason it looks like you want to avoid passing arguments in to CreateInternal() – AdaRaider Aug 22 '18 at 05:04
  • It is possible to have those two implementations of `A::CreateInternal()` in two different Projects. However, it's not clear from your post how you wish to use `pData`. There might be a different/better implementation depending on how you are planning on using `pData`. – R Sahu Aug 22 '18 at 05:10
  • Your best option will depend on what you want to do with the `pData` pointer. Presumably it gets used somewhere outside the duration of the one `CreateInternal` function? – aschepler Aug 22 '18 at 05:11
  • In that case, your best option is probably a class template. – R Sahu Aug 22 '18 at 05:19
  • I am sorry, actually, pData is member data in Class A. pData -> m_pData in Class A. and the type of m_pData' is base class of B and C. and m_pData is not used in outside. – 이준학 Aug 22 '18 at 05:21
  • I want to use flag. However, Class A has to know B and C. i don't want it, because it makes mutual reference(?). is it possible to new C in CreateInenral Class A without information about C? if Class A is template class, then i think it is possible... But if Class A is not template class, it makes error in build time. – 이준학 Aug 22 '18 at 05:32
  • I want to register Class C to a particular member data in Class A. So after i make A instance, i register C to the member data, and do CreateInternal(). – 이준학 Aug 22 '18 at 05:35
  • 1
    Please use a descriptive title, readers should have some idea what you're asking about. – Passer By Aug 22 '18 at 07:07
  • I'm sorry. I'll be careful from now on. – 이준학 Aug 22 '18 at 23:16

1 Answers1

4

As denoted in the comments:

  1. You don't want to make A a template class.
  2. You the classes you want to create instances of share a common base class, which is the type of the member pointer as well (precondition for the following!).

So you can make createInternal a template function instead:

template <typename T>
void A::createInternal()
//   ^^^ A still is a normal class!
{
    m_data = new T();
    // (will fail if T is a different type NOT inheriting
    // from the base class in question)
}

Side note: Normally, if template functions are provided inside classes, you would have to implement them in the header file (typically at least, there are ways around...), if createInstance is private, though, and you don't call it from public inline functions, you can safely implement it in the source file as well as this file will be the only location the template ever is used.

You can even go a step further and allow to call all possible constructors by use of a variadic template:

template <typename T, typename ... TT>
void A::createInternal(TT&& ... tt)
{
    m_data = new T(std::forward(tt)...);
    // (will fail, if there is no matching constructor
    // for the arguments provided)
}

OK, you now have to specify which instance you want to create:

void A::f()
{
    createInternal<B>();
    createInternal<C>();
}

I assume this being acceptable as you need to be able to tell somehow which type you actually want to create.

The alternative would be different functions for different classes (createInternalB, createInternalC), but that certainly is less elegant.

Side note:

Is it possible??

Even if your classes did not share a common base class, you still could store your objects created in a std::variant or, if you happen to compile pre-C++11, even in a union (but you need to take care yourself about which type actually is stored – in this respect, std::variant is just much safer, so prefer it whenever possible).

Update on new comments:

If you want to be able to create arbitrary objects within A without having to know which one actually is created, now offering two further alternatives:

In first variant you could make createInternal virtual in A (possibly even pure virtual) and then override in derived classes to provide the objects suiting your needs.

In second variant you provide an instance of some object provider class; assuming D is the base class of both B : public D and A : public D, so then the solution might look similar to this:

class A
{
public:
    class ObjectProvider
    {
    public:
        virtual ~ObjectProvider() = default;
        public: virtual D* createInstance() = 0;
    };
    template <typename T>
    class TheObjectProvider : public ObjectProvider
    {
        public:
        D* createInstance() override
        {
            return new T();
        }
    };

    A(std::unique_ptr<ObjectProvider> p)
        : objectProvider(std::move(p))
    { }

private:
    std::unique_ptr<ObjectProvider> objectProvider;
    std::unique_ptr<D> m_data;

    void createInternal()
    {
        m_data = objectProvider->createInstance();
    }
};

Usage:

A ab(std::make_unique<A::ObjectProvider<B>());

// your own custom object provider:
class OP : public A::ObjectProvider
{
public:
     C* createInstance() override { return new C(); }
};
A ac(std::make_unique<OP>());

The std::unique_ptr as parameter (although in general, it is not recommended to use smart pointers for) now has an important function: It indicates that the class will take ownership of the object passed, i. e. is a sink in Herb Sutter's wording – see his GotW91).

If you have a most commonly used data type (e. g. B), you could provide an overloaded default constructor providing an object provider for this type (or possibly the base type D, if not being abstract).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • Thank you for your answer. I have to study about std::variant. And.. If i make createInternal to template function, when i call this, i have to insert createInternal or createInternal, right? then, it can make mutual reference because A, B and C are located in difference project. It is my fault that I inaccurately asked. – 이준학 Aug 22 '18 at 23:25
  • The CreateInternal() is called in a member function of Class A – 이준학 Aug 22 '18 at 23:38
  • `std::variant` is an option if you don't have a common base class (which you did not mention in the question itself). If you want to be able to store *any* object, there additionally is [`std::any`](https://en.cppreference.com/w/cpp/utility/any). Coming back to your comments, then better stick with the base class pointer, that will be much easier... – Aconcagua Aug 23 '18 at 07:14