2

I'm trying to make a type which can type-safely encapsulate arbitrary types. I got the idea in my head that this might be possible from this answer: 5 years later, is there something better than the "Fastest Possible C++ Delegates"? So far I have only succeeded in moving the problem, but I ran into an error that I can't find the root of.

The compiler seems to be telling me that it can't cast the value to the value's own type, which strikes me as bizarre.

I am running Mac OS X 10.6 with llvm-gcc 4.2 (gcc 4.2.1 front-end).

Suggestions of how to get rid of the void* or move it to a less consequential position are welcome, but this question isn't really about that.

The error:

$ g++ main.cpp
main.cpp: In static member function ‘static Stamp StampFactory<T>::make(T*) [with T = int]’:
main.cpp:33:   instantiated from ‘Stamp makeStamp(T*) [with T = int]’
main.cpp:39:   instantiated from here
main.cpp:26: error: could not convert template argument ‘t’ to ‘int*’

The code:

typedef void (*VoidFunc)(void*);

struct Stamp
{
    Stamp(VoidFunc p)
    {
        this->press = p;
    }
    VoidFunc press;
};

template<typename T>
struct StampFactory
{
    template<T* rvalue>
    struct Pattern
    {
        void operator()(void* lvalue)
        {
            *dynamic_cast<T*>(lvalue) = *rvalue;
        }
    };

    static Stamp make(T* t)
    {
        return Stamp(Pattern<t>()); // 28
    }
};

template<typename T>
Stamp makeStamp(T* t)
{
    return StampFactory<T>::make(t); // 33
}

int main(int argc, char** argv)
{
    int i = 0;
    Stamp s = makeStamp(&i); //39
}
Community
  • 1
  • 1
Grault
  • 974
  • 12
  • 31
  • 6
    This might be stupid but shouldn't Pattern be Pattern seeing as t is the function parameter's name? – Borgleader Sep 14 '12 at 15:18
  • @Borgleader: Looking at the declaration of Pattern, I think it is not a typo. – Mikael Persson Sep 14 '12 at 15:26
  • @Borgleader I'm passing t as a non-type template argument of type T*. That part is right, but it's not very easy to understand; I've spent several hours wrapping my head around what the answerer of the other question did for a similar effect. – Grault Sep 14 '12 at 15:26
  • The value of `t` is not known at compile time..? – BoBTFish Sep 14 '12 at 15:27
  • 1
    Non-type template parameters must be compile-time constants. Also, `dynamic_cast` only works if its operand is a reference or pointer to a polymorphic type, and never works with `void*`. – Mike Seymour Sep 14 '12 at 15:29

1 Answers1

4

The error is due to the fact that template arguments must be compile-time constants (or constexpr), and thus, cannot be a variable (or function parameter). It is allowed to have a pointer as a template argument, but there isn't much that you can feed to it because it needs to be a compile-time constant pointer value (and the only thing I can think of that qualifies is a char-pointer to a string literal). The general rule is simple: All template arguments must be known at compile-time, whether it is a type or a value. This excludes function parameters or other kinds of run-time variables.

I wish I could suggest an alternative to achieve what you desire, but I can't understand what you are actually trying to do at all.

Mikael Persson
  • 18,174
  • 6
  • 36
  • 52
  • The root issue is that I have a project in which I'm trying to pass a map of strings to arbitrary data, and I'm trying to avoid void* but I guess it's necessary. I wanted to make a type which would not require that user code know the type of the contained value (so disparate types can be grouped together in a map), and would allow user code to retrieve that value by passing a reference or pointer of a type determined by prior human knowledge of the type. – Grault Sep 14 '12 at 15:42
  • 1
    @Jesdisciple Have a look at Boost.Any (www.boost.org/libs/any/index.html) or Boost.Variant (www.boost.org/doc/html/variant.html), it seems to be what you are looking for. – Mikael Persson Sep 14 '12 at 15:51