views:

281

answers:

5

Hi,

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.

+5  A: 

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.

Dirk
+6  A: 

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.

Jitse Niesen
Any POD type is allowed as a template parameter. A string for instance isn't.
the_drow
Just because POD means "plain old datatype" doesn't mean that PODs may be used anywhere. POD structs aren't allowed as template parameters.
Johannes Schaub - litb
bool is an integral type, so is allowed to be a 'non-type' template parameter. Non-type template parameters can be integral types, enumerations, pointers (to object, function or member) or references (to object or function)
Michael Burr
+4  A: 

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.

Johannes Schaub - litb
I'm wrapping this with boost:bind and I can't seem to get references to work.
jameszhao00
Then i recommend you to ask another question to get that right first, before wasting time on this stuff
Johannes Schaub - litb
@jameszhao00, I think that my answer will give you ability to use boost::bind.
Kirill V. Lyadvinsky
A: 

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

gatorfax
Yea I was thinking that too. I purposely duplicated parts of code so the branches are totally separated.
jameszhao00
By the way, I ran valgrind/cachegrind on my app and it turns out that comparison wasn't optimized away.
jameszhao00
+2  A: 

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
}
Kirill V. Lyadvinsky