Call template builder in non-template class from daughter class

1

I'm doing a wrapper in C ++ for a C library ( libuv , in case there is curiosity).

To keep the code as portable as possible, I am using pointers at the original struct .

Each type of struct has its own initialization function; but, in my wrapper , I start from a base class that contains in a union all possible types of pointers to use.

To initialize those pointers, I have to make a call to a specific C function; for this, my base class is not template , but has a template builder ; the idea is that, from each daughter class, the specific specialization to be used in each case is called.

However, I'm having trouble calling the base-class template-builder:

struct uv_loop_t {
  void *data;
};

struct uv_tcp_t {
  void *data;
};

void uv_loop_init( uv_loop_t * );
void uv_tcp_init( uv_tcp_t * );

namespace uv {

class uvbase {
protected:
  static uv_loop_t *currentLoop;

  union {
    uv_loop_t *loop;
    uv_tcp_t *tcp;
  };

  template< typename T > uvbase( void * = nullptr );

public:
  uvbase( ) = delete;
};

class uvloop: public uvbase {
public:
  uvloop( void *d = nullptr ) : uvbase< ::uv_loop_t >( d ) { }
};

}

template< > uv::uvbase::uvbase< uv_loop_t >( void *d ) {
  loop = new uv_loop_t;
  uv_loop_init( loop );
  loop->data = d;
}

template< > uv::uvbase::uvbase< uv_tcp_t >( void *d ) {
  tcp = new uv_tcp_t;
  uv_tcp_init( tcp );
  tcp->data = d;
}

To prove that it is correct, I executed

  

g ++ -std = c ++ 11 -Wall -Wextra -pedantic -c test.hpp

And I got a pretty sequence of errors:

  

test.hpp: In constructor 'uv :: uvloop :: uvloop (void *)':
  test.hpp: 31: 33: error: 'class uv :: uvbase uv :: uvbase :: uvbase' is not a non-static data member of 'uv :: uvloop'
          uvloop (void * d = nullptr): uvbase < :: uv_loop_t > (d) {}

     

test.hpp: 31: 39: error: expected '(' before '<' token
     uvloop (void * d = nullptr): uvbase < :: uv_loop_t > (d) {}

     

test.hpp: 31: 39: error: use of deleted function 'uv :: uvbase :: uvbase ()'
  test.hpp: 26: 3: note: declared here
     uvbase () = delete;

     

test.hpp: 31: 39: error: expected '{' before '<' token
     uvloop (void * d = nullptr): uvbase < :: uv_loop_t > (d) {}

Next, I changed my class uvloop to

class uvloop: public uvbase {
public:
  uvloop( void *d = nullptr ) : uvbase::uvbase< ::uv_loop_t >( d ) { }
};

in an attempt to indicate explicitly that I want to call a template constructor.

The results did not improve much:

  

test.hpp: In constructor 'uv :: uvloop :: uvloop (void *)':
  test.hpp: 31: 47: error: expected template-name before '<' token
     uvloop (void * d = nullptr): uvbase :: uvbase < :: uv_loop_t > (d) {}

     

test.hpp: 31: 47: error: use of deleted function 'uv :: uvbase :: uvbase ()'
  test.hpp: 26: 3: note: declared here
     uvbase () = delete;

     

test.hpp: 31: 47: error: expected '{' before '<' token
     uvloop (void * d = nullptr): uvbase :: uvbase < :: uv_loop_t > (d) {}

  • How do I call a specific specialization of the constructor of the base class from the constructor of the daughter class?
asked by Trauma 05.11.2017 в 09:32
source

2 answers

2

Problem.

It's a curious problem, reproducible with the following code:

struct base { template <typename T> base() {} };
struct derivada : public base {};

We are indicating that the class base has a default constructor, that we even define, it should not be a problem to create an instance derivada with constructor by default since it would call the default constructor of base .

But, the problem is that base has default constructor (constructor without parameters) template and as such, does not exist until it is instantiated, as an additional problem the fact that it has a defined constructor (although nonexistent) makes other constructors are not considered so that the true default constructor is considered deleted, since the base class does not have a default constructor the derivative can not be built.

Solution.

One solution that we could think of is to indicate the template parameter of the base class in the derived classes:

struct base { template <typename T> base() {} };
struct derivadaint : public base<int> {};
struct derivadachar : public base<char> {};

But it fails because that syntax tells the compiler that the class base is template, when it is not true, the template is only the constructor not the entire class.

Thus, the only way to disambiguate the template constructor of the base class is to instantiate it, at that moment the underlying type of the template can be deduced, but since there are no parameters, the constructor of the base class does not have any type on which to make deductions!.

Therefore, there is only the alternative of adding a parameter to the constructor so that the type of the template can be deduced:

struct base { template <typename T> base(const T &) {} };
// parametro auxiliar para forzar deuccion --> ^^^

struct derivadaint : public base { derivadaint() : base(int{}) {} };
//                   usa el constructor base de int --> ^^^^^
struct derivadachar : public base { derivadachar() : base(char{}) {} };
//                    usa el constructor base de char --> ^^^^^^
    
answered by 05.11.2017 / 22:53
source
3

The problem is that you can not specify the template argument in the constructor call. It's that, literally, you have nowhere to put it; you must rely on the automatic deduction of the type.

I think there must be some other solutions, including the use of inheritance, but relying on the automatic deduction of types, a solution I propose is:

struct uv_loop_t {
    void *data;
};

struct uv_tcp_t {
    void *data;
};

void uv_loop_init(uv_loop_t *) { }
void uv_tcp_init(uv_tcp_t *) { }

namespace uv {

    class uvbase {
    protected:
        static uv_loop_t *currentLoop;

        union {
            uv_loop_t *loop;
            uv_tcp_t *tcp;
        };

        template<typename T> uvbase(T *d = nullptr) { };
        template<> uvbase(uv_loop_t* d);
        template<> uvbase(uv_tcp_t* d);

    public:
        uvbase() = delete;
    };

    class uvloop : public uvbase {
    public:
        uvloop(uv_loop_t *d = nullptr) : uvbase(d) { }
    };

    class uvtcp : public uvbase {
    public:
        uvtcp(uv_tcp_t *d = nullptr) : uvbase(d) { }
    };
}



template<>
uv::uvbase::uvbase(uv_loop_t *d)
{
    loop = new uv_loop_t;
    uv_loop_init(loop);
    loop->data = d;
}

template<>
uv::uvbase::uvbase(uv_tcp_t *d)
{
    tcp = new uv_tcp_t;
    uv_tcp_init(tcp);
    tcp->data = d;
}
    
answered by 05.11.2017 в 19:40