0

I have three pointers within a class, each of which are instantiation of a templated structure. I am trying to retrieve either of them using a get<>() method whose return type differs accordingly.

//Example program
#include <iostream>
#include <map>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/variant.hpp>
#include <boost/any.hpp>
class CBase {};
enum en {Aa = 1, Bb = 2, Cc = 3};
class A {
    public: 
    template<en dim>
    struct C : public CBase, boost::static_visitor<CBase *>{
        int x;
        C() {
          x = dim;
        }
        template <en t>
        CBase * operator()(C<t> *s) const {
          return s;
        }
    };

    C<Aa>* Aasd;
    C<Bb>* Bbsd;
    C<Cc>* Ccsd;
    std::map<en, boost::variant<C<Bb> *, C<Aa> *, C<Cc> * > > matrices;    

    A() {
        Aasd = new C<Aa>;
        Bbsd = new C<Bb>;
        Ccsd = new C<Cc>;
    matrices.insert(std::make_pair(Bb, Bbsd));
    matrices.insert(std::make_pair(Aa, Aasd));
    matrices.insert(std::make_pair(Cc, Ccsd));
   }

   template<en tt>
    C<tt>* get() {
     return static_cast<C<tt> *>(boost::apply_visitor(C<tt>(), matrices[tt]));   
    }

    ~A() {
     delete Aasd;
     delete Bbsd;
     delete Ccsd;
    }        
};

    template<>
    A::C<Aa>* A::get<Aa>() {
               return static_cast<C<Aa> *>(boost::apply_visitor(C<Aa>(), matrices[Aa]));
    }

    template<>
    A::C<Bb>* A::get<Bb>() {
               return static_cast<C<Bb> *>(boost::apply_visitor(C<Bb>(), matrices[Bb]));
    }

    template<>
    A::C<Cc>* A::get<Cc>() {
               return static_cast<C<Cc> *>(boost::apply_visitor(C<Cc>(), matrices[Cc]));
    }

int main()
{    
  A a;

  int i = 0;
  en samp = Aa;
  std::cout<<a.get<Aa>()->x<<std::endl; // This runs fine
  //std::cout<<a.get<samp>()->x<<std::endl; // This throws error: the value of 'samp' is not usable in a constant expression
  return 0;
}

I do understand that I need to specify a compile time constant to instantiate a template. However, in my case I would like to use a variable to retrieve either of the pointers. Any thoughts or suggestions on how to do that would be highly appreciated.

EDIT:

I am looking for alternate suggestions to retrieve the 'x' member of these 3 pointers Aasd, Bbsd, Ccsd as in the main function even if I have to completely remove the templates. It should be probably something like

en samp = Aa;
a.get(samp)->x = 6;
samp = Bb;
a.get(samp)->x = 5;

or

en samp = Aa;
a[samp]->x = 6;
samp = Bb;
a[samp]->x = 5;
ZincFur
  • 49
  • 9
  • If you know you need a compile time constant, how do you think using a variable could work? – Barry May 19 '16 at 14:38
  • Templates must be solved at compile-time, but your `samp` argument has value determined at run-time so can't be used at compile-time. Using templates is a kind of programming for which execution is made at compile-time. – Jean-Baptiste Yunès May 19 '16 at 14:42
  • Apologies. May be I should have made this more clear in my question. I do understand that using a variable is not going to work for obvious reasons. All I am looking is for alternate suggestions to retrieve the pointers Aasd, Bbsd and Ccsd using a variable as in the main function. – ZincFur May 19 '16 at 14:47
  • @ZincFur, perhaps replace `Aa` with `runtime_function()` in the question to make it clear that it's not a constant – Aaron McDaid May 19 '16 at 14:57
  • Retrieving the pointer will be possible, but the really big challenge is "where to store the result?". The return type of `get` cannot change at runtime. You'll need to think about the whole design here – Aaron McDaid May 19 '16 at 15:00
  • @AaronMcDaid The struct C in actual contains a set of Eigen Matrices whose dimension are specified by the enum en. In my main function, based on what the user is inputting, I need to perform some matrix operations on the members of either of these pointer objects. Though the three pointer objects have matrix of different dim, after the matrix operations all of their results are going to be of same dimension. So, instead of using some ugly if else code, I thought of accessing the members as above. I ended up with the template code as above. – ZincFur May 19 '16 at 15:13
  • As you had rightly pointed out, I had been battling with run time return type. Any suggestions or ideas to look at. I would be happy to discard this and start from something else. – ZincFur May 19 '16 at 15:15

2 Answers2

1

Make samp constant:

const en samp;

EDIT: There is why it works:

Because

Non-type Template argument has to be constant expression (known at compile time)

proof: N3337 14.3.2/1

A template-argument for a non-type, non-template template-parameter shall be one of:

...

  • for a non-type template-parameter of integral or enumeration type, a converted constant expression of the type of the template-parameter; or

...

And when is enumeration type variable constant expression?

N3337 5.19/2:

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression

...

  • an lvalue-to-rvalue conversion (4.1) unless it is applied to
    • a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression, or

...

and because in case of:

const en samp = Aa, samp refers to const object initialized with constant expression

PcAF
  • 1,975
  • 12
  • 20
  • Thanks for the answer. I just edited my question that I want to pass a variable and not a constant. – ZincFur May 19 '16 at 14:55
  • @ZincFur: If its a variable that it's not a compile time defined object and thus can't be used to instantiate a template. The only way the compiler can know that it is defined at compiler time is if you can not change it at run time. – Martin York May 19 '16 at 15:03
  • @TobySpeight Better now? – PcAF May 19 '16 at 18:07
0

Move x into CBase, and then return CBase * from get instead of C<T> *. That will at least fix the problem with the return value, allowing the calling code to access ->x easily.

Then you can have a simple method something like this (untested):

CBase* A::get(en x) {
    switch(x) {
        break; case Aa: return this->get<Aa>();
        break; case Bb: return this->get<Bb>();
        break; case Cc: return this->get<Cc>();
    }
}
Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • Although, this might work only if I move x to the base class fom C. In my case, as I had explained in the comments under my question, the members of the C are all matrices and need to be instantiated through a template argument. In that case, again I have to downcast the CBase* to the particular derived class. This becomes the chicken and egg problem. – ZincFur May 19 '16 at 15:36