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

Was it helpful?

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.

See it in action!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top