2

I want to omit some template parameter T1,T2 when create an instance of a class DeriveGenerator<T3,T4,T1,T2> to comfort my life.

Here is the ultimately simplified version of what I am encountering.

My library:-

The important part is the class declaration. (this line)
Their internal content is just a filler.

template<class T1,class T2>class BaseGenerator{ //<-- this line
    public: std::pair<T1*,T2*> generateBase(){ 
        /** actually create T3,T4 internally */
        return std::pair<T1*,T2*>(nullptr,nullptr);
    }
};
template<class T3,class T4,class T1,class T2>class DeriveGenerator{ //<-- this line
    public: Base<T1,T2>* base;
    public: std::pair<T3*,T4*> generateDerive(){ 
        auto pp=base->generateBase();
        return std::pair<T3*,T4*>((T3*)(pp.first),(T4*)(pp.second));
    }
};

User:-

class B1{};class B2{};
class B3:public B1{};
class B4:public B2{};
int main() {
    //v this is what I have to
    BaseGenerator<B1,B2> baseGen;
    DeriveGenerator<B3,B4,B1,B2> deriveGen;  //so dirty #1
    deriveGen.base=&baseGen;
    deriveGen.generateDerive();
}

Question

Is it possible to make the line #1 cleaner?
I want the type of deriveGen depends on the type of baseGen.

Here is what I wish for :-

BaseGenerator<B1,B2> baseGen;
DeriveGenerator<B3,B4> deriveGen;   //<-- modified
deriveGen.base=&baseGen;

or at least something like:-

BaseGenerator<B1,B2> baseGen;
DeriveGenerator<B3,B4, DECLARATION_TYPE(baseGen) > deriveGen;   //<-- modified
deriveGen.base=&baseGen;

I have read (still no clue):-

I don't even know if it is possible.

"decltype" seems to be the closest clue, but I can't find a way to apply it to this case.
I think I may have to split it to T1,T2.... (?)

Edit

In real case, baseGen is a non-static field of some classes that is not instantiated yet e.g.

class Holder{
    public: BaseGenerator<B1,B2> baseGen;
};

Therefore, at the time of declaring deriveGen, I can't reach the real instance of baseGen.
That is the hard part.

I can refer baseGen's type via decltype, though.
(sorry for not mention about it)

Community
  • 1
  • 1
javaLover
  • 6,347
  • 2
  • 22
  • 67

3 Answers3

5

If you can change DeriveGenerator definition to:

template<class T3, class T4, class BaseT>
class DeriveGenerator{
public:
    BaseT* base = nullptr;

    std::pair<T3*, T4*> generateDerive(){ 
        auto pp = base->generateBase();
        return {(T3*)(pp.first), (T4*)(pp.second)};
    }
};

You might use:

BaseGenerator<B1, B2> baseGen;
DeriveGenerator<B3, B4, decltype(baseGen)> deriveGen;

Else I suggest to create an helper:

 template <typename B3, typename B4, typename B1, typename B2>
 DeriveGenerator<B3, B4, B1, B2>
 MakeDerived(BaseGenerator<B1, B2>& baseGen)
 {
     DeriveGenerator<B3, B4, B1, B2> deriveGen;
     deriveGen.base = &baseGen;
     return deriveGen;
 }

and then use

BaseGenerator<B1,B2> baseGen;
auto deriveGen = MakeDerived<B3, B4>(baseGen);
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Unfortunately, in my case, I can't get an instance of `baseGen` at the time `deriveGen` is declared, and I also want to refer to `B1` and `B2` directly. Thank. – javaLover Apr 20 '17 at 11:57
  • 1
    You might add `using B1_type = B1;` in `BaseGenerator`, so you might use `BaseT::B1_type` later (as `std::vector::value_type`). – Jarod42 Apr 20 '17 at 19:19
  • Else there is still the partial specialization as shown by max66. – Jarod42 Apr 20 '17 at 19:19
  • This solution is very useful. It is pity that I can accept only one solution, sorry. – javaLover Apr 21 '17 at 08:19
2

Not sure to understand what you want but... I suppose you can define DeriveGenerator in this way

template <typename, typename, typename>
class DeriveGenerator;

template <typename T3, typename T4, typename T1, typename T2>
class DeriveGenerator<T3, T4, BaseGenerator<T1, T2>>
 {
   public:
      BaseGenerator<T1,T2>* base;

   public:
      std::pair<T3*,T4*> generateDerive ()
       { 
         auto pp=base->generateBase();
         return std::pair<T3*,T4*>((T3*)(pp.first),(T4*)(pp.second));
       }
 };

and use it as follows

BaseGenerator<B1,B2> baseGen;
DeriveGenerator<B3,B4,decltype(baseGen)> deriveGen; 

This if you're interested in T1 and T2 types; if you're only interested in BaseGenerator<T1, T2> you can simply write

template <typename T3, typename T4, typename Tbase>
class DeriveGenerator
 {
   public:
      Tbase * base;

   public:
      std::pair<T3*,T4*> generateDerive ()
       { 
         auto pp=base->generateBase();
         return std::pair<T3*,T4*>((T3*)(pp.first),(T4*)(pp.second));
       }
 };
max66
  • 65,235
  • 10
  • 71
  • 111
  • What is the line `template class DeriveGenerator;` called? Forward declaration? I have never known I don't need to fully define the base case. I just [experiment](http://ideone.com/hoRYNb) about this specifically , and it seems to be compilable. – javaLover Apr 20 '17 at 11:54
  • 1
    @javaLover - if I'm not wrong (but take in count that I'm poor in terminology) is a class "declatarion"; the following `template class DeriveGenerator> { /* ... */ };` is a "definition" of a partial specialization of the class. You don't need the definition of the generic version of the class if the specialization(s) cover all used cases. – max66 Apr 20 '17 at 12:09
  • Wow, new knowledge for me. Thank. – javaLover Apr 20 '17 at 12:10
1

I think it's simpler to push the T1 and T2 template parameters for DeriveGenerator into the actual generateDerive method itself:

template<class T3,class T4>
class DeriveGenerator{ 
    public: 
    template<class T1, class T2, template<class, class> class Base>
    std::pair<T3*,T4*> generateDerive(Base<T1, T2>* base){
        static_assert(std::is_base_of<T1, T3>::value && std::is_base_of<T2, T4>::value, "T1 should be base of T3 and T2 should be base of T4"); 
        auto pp=base->generateBase();
        return std::pair<T3*,T4*>((T3*)(pp.first),(T4*)(pp.second));
    }
};

Demo

Now you can call it like so:

BaseGenerator<B1,B2> baseGen;
DeriveGenerator<B3,B4> deriveGen;
deriveGen.generateDerive(&baseGen);

I added a static_assert to DeriveGenerator::generateDerive that ensured proper base/derived pairing using std::is_base_of so that you'll get a compiler error if you accidentally mess up the template parameters.

class B5{}; // new class B5 nobody inherits from
BaseGenerator<B1, B5> bad_baseGen;
deriveGen.generateDerive(&bad_baseGen);
AndyG
  • 39,700
  • 8
  • 109
  • 143
  • I don't want to pass `&baseGen` every time, so I have to cache them. But thank! It is nice to view the problem from another perspective. – javaLover Apr 20 '17 at 12:08