5

How to make a template type more specific internally to help content assist?

template<class T>class B{  //note: in real case, it has more template parameter
    public: void f(){}
};
template<class B1>class C{ //<-- I know for sure that B1 derived from "B<something>".    
    B1* b;
    void test(){
        b->
             ^ ctrl+space doesn't show f()
    }
};

My poor workaround is to create template specialization at class C, but it will confuse content-assist in another way.

Below is another workaround but it is quite tedious.
I have to reflect the template argument at B and use such reflect at C one-by-one.

template<class T>class B{  
    public: using reflectiveT=T;
    /* other T e.g. reflectiveT2=T2 , ... */
    public: void f(){}
};
template<class B1>class C{ 
    using BX=B<B1::reflectiveT>; //B<B1::reflectiveT1,..T2,...T3> ... tedious
    BX* b;
    void test(){
        b->
             ^ ctrl+space will show f()
    }
};

Issues:

  • It suffer maintainabilty problem, when I want to refactor B to have more/less template arguments later.
  • If BX is happen to be a class that derived from B<something>, BXT will != BX.

I dream for something like :

template<class B1>class C{ 
    using BX=B<...> as base of B1;   //????
};

I may too rely on content-assist, but it greatly helps me to code very complex classes.

Edit

I can't just pass Args inside B as template parameter of C , because C may act wrong.

For example, B<D>::callback will be call instead of D::callback in the below code (demo):-

class x{};
template<class T>class B{
    public: static void callback(){ std::cout<<"B<D>::callback()";       }
};
class D : public B<D>{ //
    public: static void callback(){ std::cout<<"D::callback()"; }
};
template<class... Args>class C{ 
    using BX=B<Args...>;
    BX* b;
    public: void test(){
        BX::callback(); 
        //^ will invoke B<D>::callback (wrong)
        //  instead of D::callback
    }
};
int main(){
    C<D> c;  c.test();  //print "B<D>::callback()"
}

Edit: Simplify question a lot.

javaLover
  • 6,347
  • 2
  • 22
  • 67
  • If you're talking about Intellisense, I'm not sure if this is a feature. – Hatted Rooster May 18 '17 at 07:31
  • @Gill Bates Yes, I am talking about Intellisense or any plug-in that can help me in Visual-Studio (reshaper/visual-assist). I edited the tag, thank. – javaLover May 18 '17 at 07:33
  • 1
    Even if `BTX` is `B<..>`, `btx->` doesn't have necessary `f()` (because of specialization). – Jarod42 May 18 '17 at 08:14
  • @Jarod42 ... At least, make it work only in the case when there is no specialization .... or always show `f()` even there is no such function in some specialization .... I prefer a content-assist that speaks too much to a silent one. – javaLover May 18 '17 at 08:17
  • Is there any reason why you don't want to make the class `C` templated on the arguments of the class `B`? E.g. `template class C { using BXT = B; }`. – Holt May 18 '17 at 10:07
  • @Holt Good question, thank. My response can't fit here. I have add the explanation "why I can't", into the question. – javaLover May 18 '17 at 10:25

1 Answers1

4

You can separate the implementation and the interface for each function:

#include <iostream>

// Base class with default implementations and common interface
template<class T> class B {
    static void callback_impl() { std::cout<<"B<T>::callback()\n"; }
    void f_impl() { std::cout << "B<T>::f()\n"; }
public:
    static void callback() { T::callback_impl(); }
    void f() { static_cast<T*>(this)->f_impl(); }
};

class D1 : public B<D1>{ // D1 overrides callback
    friend class B<D1>;
    static void callback_impl(){ std::cout<<"D1::callback()\n"; }
};
class D2 : public B<D2> { // D2 overrides f
    friend class B<D2>;
    void f_impl() { std::cout << "D2::f()\n"; }
};

template<class... Args>class C{
    using BX=B<Args...>;
    BX b;
    public: void test(){
        BX::callback();
        b.f();
    }
};

int main(){
    C<D1> c1;  c1.test();  //print "D1::callback()\nB<T>::f()\n"
    C<D2> c2;  c2.test();  //print "B<T>::callback()\nD2::f()\n"
}
chtz
  • 17,329
  • 4
  • 26
  • 56
  • Creative indirection, thank. I believe this answer is hard to beat. While it solves a major part of problem (now it supports derived), I still have to use manual reflection, correct? – javaLover May 25 '17 at 13:25
  • Check this question on why C++ does not support reflection (by default): https://stackoverflow.com/questions/359237/why-does-c-not-have-reflection. You can make all methods of `B` vitual (with no need to templatetize it) -- this will usually result in worse runtime performance, though. E.g., you could not store `b` by value inside `C` anymore. – chtz May 25 '17 at 22:23
  • Oh, I actually like the fact that C++ does not support reflection. I just want to check that I understand both the underlying problem and content in answer correctly. I am not even sure whether the word "reflection" is appropriate here. – javaLover May 26 '17 at 01:58
  • I guess I did not understand what you mean by "manual reflection". You can of course keep your code as you originally did (with the disadvantage that your IDE won't be able to tell which methods `B1` has). Do you want something like Java's `class C >` (sorry if Java syntax is invalid, it's been a while that I used it)? There have been some attempts to introduce [Concepts](https://en.wikipedia.org/wiki/Concepts_(C%2B%2B)) into C++ – chtz May 26 '17 at 16:00
  • Op, sorry, I re-read your answer, I start to feel that your solution solve both issues. It renders all my argument/doubt that have been posted in comment here invalid. XD – javaLover May 28 '17 at 07:08