lundi 1 février 2021

input parameter in addition to variadic template arguments failed to compile

I put effort to write the example step by step to make it clear and simple to understand.

The object BaseCreator shown here exposes a create function which uses an internal class NewObject to allocate a new object of type T. The class NewObject default method is the usual way of using new operator, but it can be changed using specialization to use different process. We will see it later.

template <typename F>
class BaseCreator
{
public:
    BaseCreator() = default;
    virtual ~BaseCreator() = default;

    // create function 
    template <typename T, typename... Args>
    std::shared_ptr<T> create(Args&&... parameters)
    {
        return std::shared_ptr<T>(NewObject<T, Args...>().get(std::forward<Args>(parameters)...));
    }

private:
    // creator object with default functionality to create a new object
    template <typename T, typename... Args>
    struct NewObject
    {
        T* get(Args&&... parameters)
        {
            return new T(std::forward<Args>(parameters)...);
        }
    };
};

This works nice, and for example assume we have the follow objects types:

struct A
{
    A(int i_i, const std::string& i_s) : i(i_i), s(i_s) {}
    void addVal(int i_i) { i+=i_i; }
    int i = 0;
    std::string s;
};

struct B
{
    B(const std::vector<float>& i_v) : v(i_v) {}
    void addVal(int i_i) { v.push_back((float)i_i); }
    std::vector<float> v;
};

we can create them easily , for example:

// declare type for a creator identity 
struct Group111 {};

// creator object
BaseCreator<Group111> cCreator111;

// creating A and B
std::shared_ptr<A> spA = cCreator111.create<A>(5, "Hello");
std::shared_ptr<B> spB = cCreator111.create<B>(std::vector<float>({ 0.5f,0.1f,0.7f,0.9f }));

We can also declare a specific creation method for a specific object and specific type of creator. Here for example a specialization for creating object A using a creator of Group222:

// declare type for a creator identity 
struct Group222 {};

// specialize NewObject for BaseCreator<Group222> for creating A
template<> 
template< typename... Args> 
struct BaseCreator<Group222>::NewObject<A, Args...>
{
    A* get(int i_factor, int i_i, const std::string& i_s)
    {
        A* p = new A(i_i, i_s);
        p->i *= i_factor;
        return p;
    }
};

And creating object A using a creator of type Group222 will have a different method than the default one.

// creator object
BaseCreator<Group222> cCreator222;

// creating A with creator of Group222 needs now additional input argument 
std::shared_ptr<A> spA = cCreator222.create<A>(3, 5, "Hello");

// creating A with creator of Group222 is the same as creating using creator cCreator111
std::shared_ptr<B> spB = cCreator222.create<B>(std::vector<float>({ 0.5f,0.1f,0.7f,0.9f }));

And now for the problem :)

Now, I want to derive a new BaseCreator to have the follow one, which as you can see, has proxy create function and call its base class one with additional integer to the pack:

class Creator333 : public BaseCreator<Creator333>
{
public:
    Creator333() = default;
    virtual ~Creator333() = default;

    // creator
    template <typename T, typename... Args>
    std::shared_ptr<T> create(Args&&... parameters)
    {
        int adder = 5; // just for the example
        return std::shared_ptr<T>(BaseCreator<Creator333>::create<T>(adder, std::forward<Args>(parameters)...));
    }

};

It comes with its specialized BaseCreator<Creator333>::NewObject which should handle this additional integer for any object (in this example - assuming the object has a member function called addVal())

template<> 
template<typename T, typename... Args> 
struct BaseCreator<Creator333>::NewObject<T, Args...>
{
    T* get(int i_adder, Args&&... parameters)
    {
        T* p = new T(std::forward<Args>(parameters)...);
        p->addVal(i_adder);
        return p;
    }
};

If I will try to create an object, lets say B using this Creator333:

Creator333 cCreator333;

// creation of B using Creator333 
std::shared_ptr<B> spB3 = cCreator333.create<B>(std::vector<float>({ 0.1f,0.2f}));

I will get a compiler error:

error C2660: 'BaseCreator<Creator333>::NewObject<T,int &,_Ty>::get': function does not take 2 arguments
1>        with
1>        [
1>            T=B,
1>            _Ty=std::vector<float,std::allocator<float>>
1>        ]

But this is strange, because the function indeed should get two arguments as follows:

  1. Create333::create - gets a pack with one element std::vector({ 0.1f,0.2f})

  2. It calls its base calss BaseCreator<Create333>::create with additional integer, thus BaseCreator<Create333>::create get a pack with two elements : int(5) and std::vector({ 0.1f,0.2f})

  3. BaseCreator<Create333>::create`` calls BaseCreator::NewObject.get``` where the first element of the pack goes for the get(adder, and the other element of the pack goes for the get(Args ...

So what is the problem and how to solve it?

Thanks

Aucun commentaire:

Enregistrer un commentaire