Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding duplication of function definitions in template specializations

The class Widget has some functions that apply for all parameter types (common functions) and other functions that need to be specialized for given types (the uncommon functions).

g++ insists that the specialization for Widget should also define common_fn() and not just uncommon_fn(), but that defeats the purpose of using specialization in the first place. How can one avoid repeating common_fn()?

#include <cassert>

template<typename Type> struct Widget
{
    Widget() {}
    char common_fn() { return 'a'; }
    int uncommon_fn() { return 1; }
};

template<> struct Widget<char>
{
    Widget() {}
    int uncommon_fn() { return 2; }
};

int main()
{
    Widget<char> WidgetChar;
    assert( WidgetChar.common_fn() == 'a' ); // Error
    assert( WidgetChar.uncommon_fn() == 2 );
}

begin-edit

to Alf:

I am unable to use

template<> int Widget<char>::uncommon_fn() { return 2; }

because some of the uncommon functions need to return a trait type (and so it was excessive to simplify by making the actual type primitive).

Or is there in fact a way to make the compiler recognize typename Foo::Bar when writing

struct Foo { typedef FooBar Bar; };
template<> typename Foo::Bar Widget<Foo>::uncommon_fn() { return ....; }

?

end-edit

begin-edit2

to iammilind:

That's interesting, but I am unable to use derivation from Widget (or the possibly clearer solution of refactoring the common parts into a parent class GeneralWidget) for the same reason. The common parts are not entirely common. Their declarations and their definitions look the same, but because they use traits they are at the end quite different.

end-edit2

like image 368
Calaf Avatar asked Aug 19 '11 03:08

Calaf


2 Answers

#include <assert.h>

template<typename Type> struct Widget
{
    Widget() {}
    char common_fn() { return 'a'; }
    int uncommon_fn() { return 1; }
};

template<>
int Widget<char>::uncommon_fn() { return 2; }

int main()
{
    Widget<char> WidgetChar;
    assert( WidgetChar.common_fn() == 'a' ); // OK
    assert( WidgetChar.uncommon_fn() == 2 );
}
like image 143
Cheers and hth. - Alf Avatar answered Oct 02 '22 12:10

Cheers and hth. - Alf


First off, this isn't really a supported thing to do in C++. Template specialization requires you to redefine all the methods, unfortunately.

You can put your common methods in a base class, and then include them by inheritance, although my own experience with that has been a mixed bag (a function in the parent class just doesn't behave quite the same way as a function in the child class in all cases; friendship works strangely, and if the base class is also a template, you have to fully specify it whenever you wish to use any of its members [except on MS compilers]. Operators never work quite as well either). You can do it by copy-pasting your common methods, which is precisely what you're trying to avoid. Or, as a compromise on copy-pasting, you can have the compiler do your copy-pasting for you:

template<> struct Widget<char>
{
    #include "Widget_common.hpart"
    int uncommon_fn() { return 2; }
};

Then you make a file called "Widget_common.hpart" that contains

// This file contains common functions for the Widget<> template, and will be #included
// directly into that template; it should not be included as a standalone header.
char common_fn() { return 'a'; }

You can also just use the standard .h extension, I suppose. This is definitely preprocessor abuse, but it does what you ask for, avoids the template inheritance headaches (and they really are quite painful), and lets you keep only a single copy of your common code. And if anyone wants to criticize what a horrible kludge this is, do so with an improved solution ;).

like image 20
AHelps Avatar answered Oct 02 '22 12:10

AHelps