Pointer-to-global-instance-of-template-class as template-parameter?
-
31-05-2021 - |
Question
I am trying to implement a generic version of the code below:
#include <iostream>
class ContainerA
{
public:
ContainerA( int newData )
: mData_(newData)
{}
int mData_;
};
class ContainerB
{
public:
ContainerB( int newData )
: mData_(newData)
{}
int mData_;
};
ContainerA staticInstanceA( 3 );
ContainerB staticInstanceB( 11 );
template< ContainerA* ptrToContainer >
class WorkerOnA
{
public:
WorkerOnA( )
: mPtrToContainer_(ptrToContainer)
{}
void operator()()
{
std::cout << "Data = " << mPtrToContainer_->mData_ << '\n';
}
private:
ContainerA* mPtrToContainer_;
};
template< ContainerB* ptrToContainer >
class WorkerOnB
{
public:
WorkerOnB( )
: mPtrToContainer_(ptrToContainer)
{}
void operator()()
{
std::cout << "Data = " << mPtrToContainer_->mData_ << '\n';
}
private:
ContainerB* mPtrToContainer_;
};
int main( )
{
WorkerOnA<&staticInstanceA> workerOnAInstance;
WorkerOnB<&staticInstanceB> workerOnBInstance;
workerOnAInstance();
workerOnBInstance();
return 0;
}
What I would like to have (if this is possible at all) is a single Worker template-class, which can be instantiated to work on either container, something like:
template< ?? ptrToContainer >
class WorkerOnAnyContainer
{
public:
WorkerOnA( )
: mPtrToContainer_(ptrToContainer)
{}
void operator()()
{
std::cout << "Data = " << mPtrToContainer_->mData_ << '\n';
}
private:
?? mPtrToContainer_;
};
However, after several hours, I still can't figure what the '??'s should be. Maybe a template-wizard has an idea?
Update 1: Fixed mistake in 'operator()' of Workers (ptrToContainer -> mPtrToContainer_). Sorry for that.
Update 2: I got something working, but I would still be curious if anyone has a better idea. For example, having a single template-parameter would be nice. Does anyone know if "template template parameters" can help in this situation?
template< class TContainer, TContainer* ptrToContainer >
class Worker
{
public:
Worker( )
: mPtrToContainer_(ptrToContainer)
{}
void operator()()
{
std::cout << "Data = " << mPtrToContainer_->mData_ << '\n';
}
private:
TContainer* mPtrToContainer_;
};
Thanks, D
Solution
I'll give it a shot. How about changing your template so that it's given the type as a parameter, instead of the pointer itself? You can still pass in a pointer to the constructor:
template< typename TContainer >
class WorkerOnAnyContainer
{
public:
WorkerOnA( TContainer* ptrToContainer )
: mPtrToContainer_(ptrToContainer)
{}
void operator()()
{
std::cout << "Data = " << mPtrToContainer_->mData_ << '\n';
}
private:
TContainer* mPtrToContainer_;
};
Then you could use it like:
WorkerOnAnyContainer<ContainerA> workerOnAInstance(&staticInstanceA);
Since you want to keep the pointer-as-template-parameter design, you could go with something like this:
template< typename TContainer, TContainer* ptrToContainer >
class WorkerOnAnyContainer
{
public:
WorkerOnA()
: mPtrToContainer_(ptrToContainer)
{}
void operator()()
{
std::cout << "Data = " << ptrToContainer->mData_ << '\n';
}
private:
TContainer* mPtrToContainer_;
};
And use it like:
WorkerOnAnyContainer<ContainerA, &staticInstanceA> workerOnAInstance;
But, this is kinda messy since you need two template arguments, and the first one feels redundant. I'm not sure it's possible to solve this with C++03, but I figured it would be possible to build a helper method that can do the type deduction for us in C++11:
template<typename T>
auto CreateWorker(T* container) -> WorkerOnAnyContainer<T, container>
{
return WorkerOnAnyContainer<T, container>();
}
But, since the compiler expects the function to work for non-compile-time-const parameters, this doesn't compile (GCC 4.6.3):
use of parameter 'container' outside function body
It turns out you're not the only one trying to do this. Apparently, you can't create a helper method this way, even with C++11.
The only thing I can think of that actually works is to use a macro (I know, I know):
#define CreateWorker(container) WorkerOnAnyContainer<decltype(container), &container>()
Then using it is as simple as:
auto worker = CreateWorker(staticInstanceA); // Note no `&'
This makes use of auto
and a simple decltype
, both C++11 features that the Intel C++ compiler supports as of v12 (though I haven't tested this code with anything except GCC). Being a macro, it is, of course, a bit fragile though.