1

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

Dragos
  • 47
  • 8
  • 1
    Oh, I see my edited answer is redundant given your edit :-) Hmm. template-template parameters are for when you have a type that takes a template parameter, and you're using that type as a template. I don't think it would apply here. I tried writing a type-inferring helper function, but that doesn't work either because the pointer value is part of the worker type signature. +1, this is more interesting than I first thought. – Cameron Apr 15 '12 at 18:43
  • @Cameron My "production" compiler is Intel's, which has some C++11 features: http://software.intel.com/en-us/articles/c0x-features-supported-by-intel-c-compiler/. Which new features would be required for combining the template parameters? – Dragos Apr 15 '12 at 19:06

1 Answers1

3

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!

Community
  • 1
  • 1
Cameron
  • 96,106
  • 25
  • 196
  • 225
  • Thank you for the reply. Indeed, this is what I was using until now. However, this is part of a larger numerical model, and for performance reasons I now want to ensure that the pointer to the container is embedded inside the Worker at compile-time, to help the compiler inline some methods (omitted in my example) from the Container-class, which are called from the Workers. Any more ideas? – Dragos Apr 15 '12 at 16:49
  • Thank you very much for the answer! It seems we came up with the same solution, but did not synchronise. Do you know if it is possible to combine the two template parameters? This is a side-issue, of course, but I think it may make life of my users easier :). – Dragos Apr 15 '12 at 18:51
  • @Dragos: OK, this is much trickier than I thought :-( The only (compiling) solution I can think of so far uses a macro (*sigh*). See my edit. – Cameron Apr 15 '12 at 19:41