jameszhao00
jameszhao00

Reputation: 7321

C++: Optimize using templates variables

Currently, I have some code as follows

template<typename Type>
Type* getValue(std::string name, bool tryUseGetter = true)
{
    if(tryUseGetter)
    {
        if(_properties[name]->hasGetter)
        {
            return (Type*)_properties[name]->getter();
        }
        return (Type*)_properties[name]->data;
    }
    else
    {
        return (Type*)_properties[name]->data;
    }
}

Is there a way to make tryUseGetter a compile time switch? i.e. move it to the template declaration so it's something akin to this

template<typename Type, bool tryUseGetter = true>
...

Thanks.

Upvotes: 3

Views: 1135

Answers (5)

Kirill V. Lyadvinsky
Kirill V. Lyadvinsky

Reputation: 99715

You could use type cast operator and structure getValue as follows (usage syntax will be the same as with function) :

template<typename Type, bool tryUseGetter = true> 
struct getValue {};

template<typename Type>
struct getValue<Type, true> {
    getValue(const std::string& name) : name(name) {};
    operator Type*() const {
        if(_properties[name]->hasGetter) {
            return (Type*)_properties[name]->getter();
        }
        return (Type*)_properties[name]->data;
    }
private:
    const std::string& name;
};

template<typename Type>
struct getValue<Type, false> {
    getValue(const std::string& name) : name(name) {};
    operator Type*() const {
        return (Type*)_properties[name]->data;
    }
private:
    const std::string& name;
};

Usage:

int main () {
    int* i = getValue<int>( "TEST" ); // true by default
    Xstruct* x = getValue<Xstruct, false>( "XS" ); // false
}

Upvotes: 2

gatorfax
gatorfax

Reputation: 1142

Before you go and make the code all complicated...did you check to see if the optimizing compiler was already doing this for you?

Upvotes: 0

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507393

Just in case you really need it (although from a performance point of view, i doubt it would be noticeable), i would overload

template<typename Type>
Type* getValue(std::string const &name)
{
    if(_properties[name]->hasGetter)
    {
        return (Type*)_properties[name]->getter();
    }
    return (Type*)_properties[name]->data;
}

template<typename Type, bool tryUseGetter>
Type *getValue(std::string const &name) 
{
    if(tryUseGetter)
    {
        return getValue<Type>(name);
    }
    else
    {
        return (Type*)_properties[name]->data;
    }
}

Also, you should first follow the real rules: Pass name by-const-reference instead of passing a copy, for example.

Upvotes: 4

Jitse Niesen
Jitse Niesen

Reputation: 4542

As an alternative to Dirk's answer, you can put the function in a struct. Template classes can be partially specialized (in contrast to template functions), so you can write:

template<typename Type, bool tryUseGetter = true>
struct getValue;

template<typename Type>
struct getValue<Type, true>
{
    Type* run(std::string name)
    {
        if(_properties[name]->hasGetter)
        {
            return (Type*)_properties[name]->getter();
        }
        return (Type*)_properties[name]->data;
    }
};

template<typename Type>
struct getValue<Type, false>
{
    Type* run(std::string name)
    {
        return (Type*)_properties[name]->data;
    }
};

Call it as getValue<T>::run("foo") or getValue<T, false>::run("foo") .

I'm not 100% certain that it's allowed to have template parameters of the type bool, so perhaps you should change it to int.

Upvotes: 6

Dirk
Dirk

Reputation: 31061

You can get compile-time dispatch of the "try-use-getter" stuff by splitting your method into two and having the compiler dispatch to the appropriate method:

struct __try_use_getter { }

external const __try_use_getter tryusegetter;

template<typename Type> 
Type* 
getValue(std::string name, const __try_use_getter&)
{
    if(_properties[name]->hasGetter)
    {
        return (Type*)_properties[name]->getter();
    }
    return (Type*)_properties[name]->data;
}

template<typename Type> 
Type* 
getValue(std::string name)
{
    return (Type*)_properties[name]->data;
}

With this scenario in place, you would have full compile-time dispatching:

int result = getValue("foo", tryusegetter);

would try the getter first, whereas

int result = getValue("foo");

would immediately call the getter-less version.

Upvotes: 5

Related Questions