CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 4 of 4
  1. #1
    Join Date
    Jan 2004
    Location
    Düsseldorf, Germany
    Posts
    2,401

    [RESOLVED] Passing a function pointer as template argument to a class

    Hi all,

    I have in the past written code for templated functions where one function argument can be either a function pointer or a Functor. Works pretty straightforward.

    Now I am in a situation where I am actually trying to pass a function pointer as template argument to a class. Unfortunately this does not work, I can pass the Functor class but not the function pointer. Below code illustrates the issue:

    Code:
    #include <string>
    #include <iostream>
    #include <sstream>
    
    #include <cstdlib>
    
    // For demonstration
    const char * external_library_call() {
    	return "FFFF";
    }
    
    // Default conversion from const char * to native type. This shall be specialized!
    template<typename T>
    class StandardConverter
    {
    public:
    	T operator()(const char * str) {
    		std::istringstream is(str);
    		T t;
    		is >> t;
    		return t;
    	}
    };
    
    // Special conversion function. There might be many of these.
    std::string hex2dec(const char * str) {
    	std::ostringstream os;
    	os << strtol(str, 0, 16);
    	return os.str();
    }
    
    // Field class
    template<typename T, typename F = StandardConverter<T> >
    class Field
    {
    private:
    	F f;
    public:
    	T get()
    	{
    		return f(external_library_call());
    	}
    };
    
    // Record class
    class Record
    {
    public:
    	Field<std::string> I_FIELD1;
    	Field<std::string, StandardConverter<std::string> > I_FIELD2;
    	// Field<std::string, hex2dec> I_FIELD3;
    };
    
    int main()
    {
    	Record rec;
    	std::cout << rec.I_FIELD1.get() << std::endl;
    	std::cout << rec.I_FIELD2.get() << std::endl;
    	// std::cout << rec.I_FIELD3.get() << std::endl;
    }
    The idea is to have the definition of the Record class simple and readable and have a maintainable way to add auto-conversion functions to the class. So the lines I commented out are the desirable way how I want my code to look. Unfortunately I could not come up with any way that was close to readable for solving this.

    Does anybody have any good suggestions?
    Last edited by treuss; August 15th, 2012 at 03:59 AM. Reason: Spelling
    More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason - including blind stupidity. --W.A.Wulf

    Premature optimization is the root of all evil --Donald E. Knuth


    Please read Information on posting before posting, especially the info on using [code] tags.

  2. #2
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,765

    Re: Passing a function pointer as template argument to a class

    You could do something like this:
    Code:
    #include <string>
    #include <iostream>
    #include <sstream>
    
    #include <cstdlib>
    
    // For demonstration
    const char * external_library_call() {
    	return "FFFF";
    }
    
    // Default conversion from const char * to native type. This shall be specialized!
    template<typename T>
    class StandardConverter
    {
    public:
    	T operator()(const char * str) {
    		std::istringstream is(str);
    		T t;
    		is >> t;
    		return t;
    	}
    };
    
    // Special conversion function. There might be many of these.
    std::string hex2dec(const char * str) {
    	std::ostringstream os;
    	os << strtol(str, 0, 16);
    	return os.str();
    }
    
    // Field class
    template<typename T, typename F = StandardConverter<T> >
    class Field
    {
    private:
    	F f;
    public:
    	Field() {}
    
    	explicit Field(F f_) : f(f_) {}
    
    	T get()
    	{
    		return f(external_library_call());
    	}
    };
    
    // Record class
    class Record
    {
    public:
    	Record() : I_FIELD3(hex2dec) {}
    
    	Field<std::string> I_FIELD1;
    	Field<std::string, StandardConverter<std::string> > I_FIELD2;
    	Field<std::string, std::string (*)(const char*)> I_FIELD3;
    };
    
    int main()
    {
    	Record rec;
    	std::cout << rec.I_FIELD1.get() << std::endl;
    	std::cout << rec.I_FIELD2.get() << std::endl;
    	std::cout << rec.I_FIELD3.get() << std::endl;
    }
    C + C++ Compiler: MinGW port of GCC
    Build + Version Control System: SCons + Bazaar

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  3. #3
    Join Date
    Oct 2008
    Posts
    1,456

    Re: Passing a function pointer as template argument to a class

    ... or to avoid laserlight's auxillary constructor and put the function name directly in the Field declaration, you could write something like

    Code:
    template<class Src, class Dst, Dst (*Fun)(Src) >
    class FunctionConverter
    {
    public:
    	Dst operator()(Src src)
    	{
    		return Fun( src );
    	}
    };
    
    class Record
    {
    public:
    	Field<std::string> I_FIELD1;
    	Field<std::string, StandardConverter<std::string> > I_FIELD2;
    	Field<std::string, FunctionConverter<const char*,std::string,hex2dec> > I_FIELD3;
    };
    moreover, if I read your code correctly, Src should be fixed to "const char*", hence it could be omitted in the declaration. Then, you could also just write, say, "FunctionField<std::string, hex2dec> I_FIELD3;" ...

  4. #4
    Join Date
    Jan 2004
    Location
    Düsseldorf, Germany
    Posts
    2,401

    Re: Passing a function pointer as template argument to a class

    Thanks Superbonzo!

    I was trying the same approach, i.e. using a generic class to wrap around the function(s) but always ended up with the same problem, i.e. how to pass the function as a template argument, be it to the wrapper or to the Field class. Looking at your example I understood it is actually possible.

    I modified the approach to not use Functors at all, which now allows for clean definition of the Record class. For those who care, an example is below.

    Code:
    #include <string>
    #include <iostream>
    #include <sstream>
    
    #include <cstdlib>
    
    // For demonstration
    const char * external_library_call() {
    	return "1010";
    }
    
    // Default conversion from const char * to native type.
    template<typename T>
    T convert(const char * str)
    {
    	std::cout << "Using generic (templated) convert function\n";
    	std::istringstream is(str);
    	T t;
    	is >> t;
    	return t;
    }
    
    // Specializations of convert
    template<>
    std::string convert<std::string>(const char * str)
    {
    	std::cout << "Using specialization for std::string\n";
    	return str;
    }
    
    template<>
    const char * convert<const char *>(const char * str)
    {
    	std::cout << "Using specialization for const char *\n";
    	return str;
    }
    
    // Special conversion function. There might be many of these.
    std::string hex2dec(const char * str) {
    	std::cout << "Using hex2dec function\n";
    	std::ostringstream os;
    	os << strtol(str, 0, 16);
    	return os.str();
    }
    
    template<typename T, T(*F)(const char *) = convert<T> >
    class Field
    {
    public:
    	T get()
    	{
    		return F(external_library_call());
    	}
    };
    
    
    // Record class
    class Record
    {
    public:
    	Field<std::string>            I_FIELD1;
    	Field<const char *>           I_FIELD2;
    	Field<std::string,  hex2dec>  I_FIELD3;
    	Field<int>                    I_FIELD4;
    };
    
    int main()
    {
    	Record rec;
    	std::cout << rec.I_FIELD1.get() << std::endl;
    	std::cout << rec.I_FIELD2.get() << std::endl;
    	std::cout << rec.I_FIELD3.get() << std::endl;
    	std::cout << rec.I_FIELD4.get() << std::endl;
    }
    More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason - including blind stupidity. --W.A.Wulf

    Premature optimization is the root of all evil --Donald E. Knuth


    Please read Information on posting before posting, especially the info on using [code] tags.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured