Sir Mate Sir Mate - 1 month ago 8
C++ Question

C++ Template trouble

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!

Answer

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

Comments