Explicit Function Template Specialization

Started by
1 comment, last by Zahlman 15 years, 8 months ago
Hi all - I'm trying to do something which seems fairly simple, but is probably wrought with Peril. I'm working on a TweakVar serialization system, and I'm starting with some templated code to print a value to a buffer. There's a little craziness because I'm really doing all the work through a templated wrapper class. The idea is that people can provide their own specializations if they want to serialize new primitive types. This is supposed to be a little simple, only for tweakvars, not for say, the entire gamestate, which supports far more complex types and such...

// pre-declaration
template <class T> void PrintVal( const T* dat, char* buf, unsigned int bufSize );

//trimmed code for explaination
class ITweakVar
{
    virtual void Print(char* buf, unsigned int bufSize)=0;
};

template <class T>
class TweakVarImpl<T>: public ITweakVar
{
  T* pRuntimeVal;  //set in a global ctor at startup

  void Print( char* buf, unsigned int bufSize )
  {
      PrintVal<T>( pRunTimeVal, buf, bufSize );
  }

};

//specializations
template <> void PrintVal<float> (const float* dat, char*buf, unsigned int bufSize)
{
	unsigned int len = sprintf( "%f", buf, *val ); 
	assert(len < bufSize-1);
}

template <> void PrintVal<int> (const int* dat, char* buf, unsigned int bufSize)
{
	unsigned int len = sprintf( "%d", buf, *val ); 
	assert(len < bufSize-1);
}


The ITweakVar class is some autoregistered/autolisted craziness that is tabulated so all the tweakvars can be dumped to a config file through a single function call ( in this case, by calling ITweakVar->Print() ). The problem comes when a tweakvar is set to a char* (for use in strings, etc).


template <> void PrintVal<char*> (const char** dat, char* buf, unsigned int bufSize)
{
	unsigned int len = sprintf( "%s", buf, *val ); 
	assert(len < bufSize-1);
}
/*
Above doesn't compile, generates a 
" void PrintVal<char*>(const char**, char*, unsigned int) 
is not a specialization of a function template "
*/


Hmm, so function specializations of pointer types seems to be problematic. I'm considering the possibility of just using overloads instead of specializations, but I kinda prefer the template syntax - it just seems clearer, and seems less likely to generate unintended incorrect type conversions. Thanks for any help!
...
Advertisement
const T * when T is a char * is not const char **, it's char * const *.
1) Like he said.
2) Where does 'val' come from?
3) If your goal is to be generic, why are you specifically requiring a char* and buffer size for your buffer? Just ask for an output iterator.
4) Why would you be using the old-fashioned printf() system to format data, using magic string codes to handle various types (and thus having to specialize the template, or overload for each one), when you could use the modern "stream" system, which is already typesafe (that stuff is already handled for you under the hood)?
5) Have you never heard of references?

Something like this should suffice:

class ITweakVar {    template <typename OutputIterator>    virtual void Print(OutputIterator) = 0;};// All you need to do is define operator<< for user-defined T's, then:template <typename OutputIterator>void Print(const T& value, OutputIterator to) {  std::string buffer = (std::stringstream() << value).str();  std::copy(buffer.begin(), buffer.end(), to);}template <class T>class TweakVarImpl<T>: public ITweakVar {    T* pRunTimeVal; // I'll assume you have a good reason for not holding the    // value locally and are instead pointing to someone else's copy. If this    // is actually a value you allocate using 'new' and which "belongs" to    // this instance, you are for 99.99% sure doing something wrong.    template <typename OutputIterator>    void Print(OutputIterator to) {        // notice the namespace resolution here, and that the template type        // can be inferred by the compiler:        ::Print(*pRunTimeVal, to);    }};// And you can even take this full circle:template <typename T>ostream& operator<<(ostream& os, ITweakVar& var) {    var.Print(ostreambuf_iterator<char>(os));    return os;}

This topic is closed to new replies.

Advertisement