views:

207

answers:

2

I have this template :

        template <class SourceFormat, class DestFormat, void (*convert)(DestFormat, SourceFormat)>
    static void _draw(...);

And these functions :

    template <class Class1, class Class2>
    inline static void convertNone(Class1& dest, Class2& source) {
        dest = source;
    };
    inline static void convertARGB_GREY(unsigned __int32& dest, unsigned __int8& source) {
        dest = source + (source << 8);
        dest += (dest << 16);
    };

I use the template in another function :

    void Blitter::draw(...) {

    if (...) {
        _draw<unsigned __int32, unsigned __int32, &convertNone>(...);
    } else {
        _draw<unsigned __int32, unsigned __int8, &convertARGB_GREY>(...); // ERRORS go here!
    }
}

I get these errors :

Error   1   error C2440: 'specialization' : cannot convert from 'void (__cdecl *)(unsigned int &,unsigned char &)' to 'void (__cdecl *const )(unsigned char,unsigned int)'  d:\projects\fanlib\source\blitter.cpp   102
Error   2   error C2973: 'FANLib::Blitter::_draw' : invalid template argument 'void (__cdecl *)(unsigned int &,unsigned char &)'    d:\projects\fanlib\source\blitter.cpp   102

I suppose it's rather obvious that I don't fully comprehend functions-as-parameters... :-(

Many thanks in advance

+5  A: 

There are several problems in your code.

In your code, when you call _draw, the SourceFormat and DestFormat parameters of _draw template are supplied with explicit arguments. These arguments are plain non-reference integer types. That automatically means that the third template parameter of _draw is supposed to be a function that takes its arguments by value as well. I.e. if SourceFormat and DestFormat is unsigned __int32, then the function pointer should have type void (*)(unsigned __int32, unsigned __int32). Instead, you are trying to supply a function that takes its arguments by reference, i.e. the pointer type is void (*)(unsigned __int32 &, unsigned __int32 &). These are completely unrealted and incompatibe pointer types. The following simple code would fail to compile for the same reason

void foo(int&);
void (*pf)(int) = foo; 
// ERROR: a value of type "void (*)(int &)" cannot be used to initialize an entity of type "void (*)(int)"

How do you expect this to work? Either remove references from actual function parameters (use return type instead), or add them in template parameter declaration.

Another problem is that you are trying to use pointers to static functions (internal linkage) to parametrize a template. This is illegal in C++. A short example that illustrates the problem might look as follows

template <void (*P)()> void foo() {}
static void bar() {}
...
foo<bar>();
// ERROR: template argument may not reference a non-external entity

If you want to parametrize a template with a pointer value, the argument you supply must point to an entity with external linkage. You compiler might allow what you did as is, but it is still illegal.

Finally, it illegal in C++ to end a standalone function definition with semicolon. This is treated as an "empty declaration" actually and there are no "empty declarations" in C++. Many compilers allow this, but it is still illegal.

P.S. Additionally, as others already noted, you managed to reverse the order of parameter types in your non-template converter function convertARGB_GREY.

AndreyT
In addition the message indicates that he has his char and int mixed up (to OP, *read* the message).
UncleBens
To UncleBens : you are right, I should read the error message more carefully. I actually stopped reading it at this point :"cannot convert from 'void (__cdecl *) ... to 'void (__cdecl *const )". Stupidly, I thought it was the 'const' that couldn't be converted! I deserve painful death :-|
Bill Kotsias
Bill Kotsias
@Bill Kotsias: While the semicolon issue can be seen as pretty harmess, the issue with template argument linkage is more serious with a rather obvious rationale behind it. It is not old, and it is very necessary. What you mean by "clean coding standards" is not exactly clear. I'd understand if the coding standard didn't mention these issues at all (I haven't seen one that would), but if the one you are using actually explicitly says they are OK... well... the garbage can is really the place where that coding standard deserves to be.
AndreyT
+2  A: 

I don't know if you've done it intentionally, but your template parameters go Source/Destintaion and then Destination/Source.

Notice that when you do _draw<unsigned __int32, unsigned __int8, &convertARGB_GREY>(...); your template definition fills them in as:

SourceFormat = unsigned __int32
DestFormat = unsigned __int8
void (*convert)(unsigned __int8, unsigned __int32)

You don't have a function of that definition though.

You do have one that matches void (*convert)(unsigned __int32&, unsigned __int8&)
Do you see how the parameters don't match?

Declare your template like this:

template <
    class SourceFormat,
    class DestFormat,
    void (*convert)(SourceFormat, DestFormat)> 
static void _draw(...);

and your function declaration like this:

void convertARGB_GREY(
    unsigned __int32 source,        // notice how I removed the '&'
    unsigned __int8 destination)    // character on these two lines

and it will compile. (Since you lose the references, I'd recommend returning the result in this case: unsigned __int8 destination convertARGB_GREY(...) though.)

Bill