1

I would appreciate some assistance in a (for my level anyways) rather complicated template problem.

Let me explain my 'system' first.

Modeling a basic audio mixing and streaming system, it has three building components:

A Buffer, from which the Player will process data from. Since they are directly connected by data, their data needs to by of the same type. Thus Buffer<T> ~ Player<T> , here the templates must match.

These are wrapped in Manager, who will eventually manage all the incoming buffers into one player.

Now, both Buffers and Players would need some different implementations, thus they are represented by generic interfaces as iPlayer and iBuffer.

My goal is to be able to declare a manager like this:

simple_manager<simple_player<float>>;

or failing this at least

simple_manager<simple_player , float>;

Since im not sure the first one even has a solution, my attempt at the second was thus:

template <typename K>
class iManager {
    private:
        K player;
};

template <template <typename> class C , typename T>
class simple_manager : iManager< C<T> > {
    public:
        void play(iBuffer<T> & _buffer,const audio_descriptor ad, bool * condition){
            player.play(_buffer,ad,condition);
        }


};

As you can see, in the concrete class, T marks the type of the data to be manipulatod, while C is the concrete class of player i wish to use. The interface has only one template which marks the concrete player class again. So K ~ C<T>

This does not compile with (only) the following error:

simple_manager.cpp: In member function ‘void simple_manager<C, T>::play(iBuffer<T>&, audio_descriptor, bool*)’:
simple_manager.cpp:18:12: error: ‘player’ was not declared in this scope
            player.play(_buffer,ad,condition);
            ^~~~~~

And i do not know what causes this. Can the compiler not deduce that T must be inherited from iPlayer, since iPlayer must implement a play() method.

I can get it to actually work if I define simple_manager like this:

class simple_manager : iManager< simple_player<float> > {...}

but it still won't work with:

class simple_manager : iManager< simple_player<T> > {...}

I am stumped. If i had <T extends iPlayer> from Java, this would work, but compilation-time templating is a tougher nut i guess.

Any help would be greatly appreciated!

Sir Mate
  • 75
  • 1
  • 6
  • I voted to reopen, as there are more issues than just the name lookup, and I think OP would like a solution for his first example too. Feel free to vote to close again if you disagree. – TartanLlama Oct 05 '16 at 14:21

2 Answers2

2

The first issue is that player is inaccessible from the derived class because it's marked private instead of protected. You could make it protected or add some protected member function to access it:

template <typename K>
class iManager {
protected:
    K player;
};

This still won't work, however, because iManager<C<T>> is a dependent base class, so its members are hidden from unqualified name lookup. To get around this, you can access it through the this pointer:

void play(iBuffer<T> & _buffer,const audio_descriptor ad, bool * condition){
    this->player.play(_buffer,ad,condition);
}

To get the nice usage in your first example, you could write a trait to extract a template argument from given type:

template <typename T> struct extract_inner;

template <template <typename> class C, typename T> 
struct extract_inner<C<T>> { using type = T; };

template <typename T> 
using extract_inner_t = typename extract_inner<T>::type;

Then that can be used to supply the correct argument to iBuffer:

template <typename T>
class simple_manager : iManager< T > {
public:
    void play(iBuffer<extract_inner_t<T>> & _buffer,
              const audio_descriptor ad, bool * condition){
        this->player.play(_buffer,ad,condition);
    }
};

Live demo

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Thanks for the fast answer. I was so tangled in a temporal template paradox i never even considered this. Also never would have thought of anything like your solution for the more elegant way, seems like magic to me. Or i still have much to learn. – Sir Mate Oct 05 '16 at 14:55
1

Your problem is quite simple.

You got almost all the syntax right, but you miss one thing: the this keyword. When you extend a class that depending on a template parameter, this become kind of a dependent name. Since the base class is a template that depends on the a template parameter on your child, the compiler must know which name come from the base and which does not. If the compiler would not request to remove the ambiguity, one could specialize your base class, throw a bunch on names and make your child class use them.

To remove the ambiguity, simply add this-> keyword in front of the name that come from the base class:

template <template <typename> class C , typename T>
struct simple_manager : iManager< C<T> > {
    void play(iBuffer<T> & _buffer,const audio_descriptor ad, bool * condition){
            this->player.play(_buffer,ad,condition);
    }
};
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141