Thread: Template method specilization

  1. #1
    Registered User
    Join Date
    Jul 2008
    Posts
    38

    Template method specilization

    Hello,

    I am writing a C++ (using C++11/0x) wrapper for Lua 5.2.

    When i saw the following Lua API functions, i thought i had a genius way of exposing them to C++:

    int lua_toboolean(int index);
    int lua_tointeger(int index);
    const char* lua_tostring(int index);
    void* lua_touserdata(int index);

    By using template function specilization i eventually came up with this (somewhat, this is a simple compilable example which demonstrates the problem):

    Code:
    #include <iostream>
    #include <string>
    
    // Just pretend that these functions are declared and defined by LUA, 
    // and that they do something actually useful.
    
    extern "C"
    {
    	int lua_toboolean(int index)
    	{
    		return 1;
    	}
    
    	int lua_tointeger(int index)
    	{
    		return 5;
    	}
    
    	const char* lua_tostring(int index)
    	{
    		return "Some string";
    	}
    
    	void* lua_touserdata(int index)
    	{
    		return reinterpret_cast<void*>(&std::cout);
    	}
    };
    
    class state 
    {
    public:
    	state() {}
    	~state() {}
    
    	template <typename T>
    	T to(int index)
    	{
    		return reinterpret_cast<T>(lua_touserdata(index));
    	}
    
    	template <>
    	int to<int>(int index)
    	{
    		return lua_tointeger(index);
    	}
    
    	template <>
    	bool to<bool>(int index)
    	{
    		return lua_toboolean(index) == 1;
    	}
    
    	template <>
    	const char* to<const char*>(int index)
    	{
    		return lua_tostring(index);
    	}
    private:
    };
    
    int main(int argc, char* argv[])
    {
    	state st;
    
    	std::cout << st.to<int>(6) << std::endl;
    	std::cout << st.to<bool>(4) << std::endl;
    	std::cout << st.to<const char*>(3) << std::endl
    }
    This way, i figured, the user of the 'to' method could use template parameters to specifiy the type, and the compiler would figure out which Lua API function to call.

    But there was a problem. This wrapper is meant to be cross platform and standard compliant, so it had to compile under GCC 4.6 as well.

    Then i found out that the 'to' method implementation was not standard. Imagine my dismay when i found i had accidentally used a Visual C++ (I am using VS2010) extension without my knowledge!

    So my question is, is there a standard way to achieve the same kind of function?

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    The error message, for those playing along at home, is "explicit specialization in non-namespace scope `class state'". A solution is to move the specializations outside of the class:
    Code:
    class state 
    {
    public:
    	state() {}
    	~state() {}
    
    	template <typename T>
    	T to(int index)
    	{
    		return reinterpret_cast<T>(lua_touserdata(index));
    	}
    private:
    };
    
    
    template <>
    int state::to<int>(int index)
    {
    	return lua_tointeger(index);
    }
    
    template <>
    bool state::to<bool>(int index)
    {
    	return lua_toboolean(index) == 1;
    }
    
    template <>
    const char* state::to<const char*>(int index)
    {
    	return lua_tostring(index);
    }

  3. #3
    Registered User
    Join Date
    Jul 2008
    Posts
    38
    Thanks, it works! Atleast with the examples it works on both compilers.

    But, it does not work with the real thing, and i am probably holding back things important to the problem.

    So here is the real class with the real problem, unfortunately, cannot be compiled by it self:

    Code:
    #ifndef BASIC_STATE_H
    #define	BASIC_STATE_H
    
    #include "luawrapper.h"
    
    #include "aux_state.h"
    
    DIGITALIS_SCRIPTING_LUAWRAPPER_NS_BEGIN
    
    template <typename CoreState, template <typename T> class AuxState>
    class basic_state : public AuxState<CoreState>
    {
    public:
    	basic_state() { }
    	basic_state(const basic_state<CoreState, AuxState>& orig) { }
    	basic_state(lua_State* L) : AuxState<CoreState>(L) { }
    	~basic_state() { }
    	
    	template <typename T>
    	T to(int index)
    	{
    		return reinterpret_cast<T>(CoreState::touserdata(index));
    	}
    private:
    };
    
    template <typename CoreState, template <typename T> class AuxState>
    template <> // Line number 28
    int basic_state<CoreState, AuxState>::to<int>(int index)
    {
    	return CoreState::tointeger(index);
    }
    
    typedef basic_state<core_state, aux_state> state;
    
    DIGITALIS_SCRIPTING_LUAWRAPPER_NS_END
    
    #endif	/* BASIC_STATE_H */
    And the full error as reported by GCC 4.6:

    Code:
    [florian@taygeta build]$ make
    [  3%] Building CXX object testing/CMakeFiles/testing.dir/testing.cpp.o
    In file included from /home/florian/gitrepos/digitalis/testing/luawrappertest.h:4:0,
                     from /home/florian/gitrepos/digitalis/testing/testing.cpp:10:
    /home/florian/gitrepos/digitalis/testing/../scripting/luawrapper/basic_state.h:28:11: error: invalid explicit specialization before ‘>’ token
    /home/florian/gitrepos/digitalis/testing/../scripting/luawrapper/basic_state.h:28:11: error: enclosing class templates are not explicitly specialized
    /home/florian/gitrepos/digitalis/testing/../scripting/luawrapper/basic_state.h:29:5: error: template-id ‘to<int>’ for ‘int digitalis::scripting::luawrapper::basic_state<CoreState, AuxState>::to(int)’
    does not match any template declaration
    make[2]: *** [testing/CMakeFiles/testing.dir/testing.cpp.o] Error 1
    make[1]: *** [testing/CMakeFiles/testing.dir/all] Error 2
    make: *** [all] Error 2
    As you can see the actual class is a little more complex then the example class.

    The 'basic_state' class inherits from 'AuxState' class (normally 'aux_state'). The AuxState class in turn inherits from CoreState (normally 'core_state').

    Any solutions for this?

  4. #4
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    O_o

    You can't directly specialize a nested template without specializing the enclosing class. (There are, believe it or not, perfectly logical reasons for this.)

    You can do a lot of things to still specialize a nested template of a template class.

    You can specialize the enclosing template. (Useless.)

    You can simply overload the nested template if it is a template function. (Perfect if it works with the design.)

    You can forward the target mechanism to a template that can be specialized normally. (Perfect if accessed through `typedef' or pointer.)

    You can forward the implementation by indirection through expanded template parameters of the enclosing template. (Impossible complex.)

    You can forward the implementation by interface, with default implementation, to a template that can be specialized normally. (Perfect if the behavior requires partial specialization.)

    Soma

    Code:
    #include <iostream>
    #include <typeinfo>
    
    template
    <
        typename T
    >
    struct interface;
    
    template
    <
        typename R
      , typename S
      , typename T
    >
    struct implementation
    {
        typedef R return_type;
        typedef S parameter_type;
        static R to(interface<T> &, parameter_type &);
    };
    
    template
    <
        typename T
    >
    struct interface
    {
        template
        <
            typename R
        >
        typename implementation<R, int, T>::return_type to
        (
            int index
        )
        {
            return(implementation<R, int, T>::to(*this, index));
        }
    };
    
    template
    <
        typename R
      , typename T
    >
    struct implementation<R, int, T>
    {
        private:
            template <typename RR, typename RS, typename RT> class THIS_VERSION_HAS_NOT_BEEN_PROVIDED;
        private:
        public:
            typedef THIS_VERSION_HAS_NOT_BEEN_PROVIDED<R, int, T> return_type;
        public:
    };
    
    template
    <
        typename T
    >
    struct implementation<int, int, T>
    {
        typedef int return_type;
        typedef int parameter_type;
        static return_type to(interface<T> &, parameter_type index)
        {
            std::cout << "doing something with " << typeid(return_type).name() << ", " << typeid(parameter_type).name() << ", and " << typeid(T).name() << '\n';
            return(return_type());
        }
    };
    
    template
    <
        typename T
    >
    struct implementation<bool, int, T>
    {
        typedef bool return_type;
        typedef int parameter_type;
        static return_type to(interface<T> &, parameter_type index)
        {
            std::cout << "doing something with " << typeid(return_type).name() << ", " << typeid(parameter_type).name() << ", and " << typeid(T).name() << '\n';
            return(return_type());
        }
    };
    
    template
    <
        typename T
    >
    struct implementation<char *, int, T>
    {
        typedef char * return_type;
        typedef int parameter_type;
        static return_type to(interface<T> &, parameter_type index)
        {
            std::cout << "doing something with " << typeid(return_type).name() << ", " << typeid(parameter_type).name() << ", and " << typeid(T).name() << '\n';
            return(return_type());
        }
    };
    
    int main()
    {
        interface<int> test1;
        interface<double> test2;
        test1.to<int>(1);
        test1.to<bool>(2);
        test1.to<char *>(3);
        test2.to<int>(1);
        test2.to<bool>(2);
        test2.to<char *>(3);
        return(0);
    }

  5. #5
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Please stop writing empty constructors and destructors. Let the compiler auto-generate these.
    I mean you're not paid by the line are you?

    As for an empty copy-constructor:
    Code:
    	basic_state(const basic_state<CoreState, AuxState>& orig) { }
    That's just evil. Do not implement these unless you absoultely have to!
    If you do not intend your class to be copyable then declare the copy-constructor AND assignment operator method as private and leave them unimplemented.
    The "rule of three" is as much about not implementing the copy-constructor, assignment operator and destructor when you don't need to, as it as about implementing all three when you definitely need one of them.
    Last edited by iMalc; 09-03-2011 at 10:38 PM.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  6. #6
    Registered User
    Join Date
    Jul 2008
    Posts
    38
    Thank you both for your replies!

    phantomotap, clearly i need read up a bit on template specialization. Despite that, i understand your suggestions and your example, i was hoping to prevent using more than one class (basic_state) to achieve what i wanted, but it seems the best solution.

    iMalc, i am still not quite sure how i should implement the copy constructors and assignment operator, since the basic_state class exposes the pointer to struct lua_State to C++. I am still not sure what the best way is, a shallow copy, deep copy or no copying at all. The basic_state class normally initializes its lua_State pointer using the Lua API function lua_newstate in the constructor and then calls 'lua_close' on the pointer in the destructor. However, Lua employs callbacks, with 'lua_State*' as parameters, for that reason a 'basic_state' object can be constructed with a lua_State pointer. When that basic_state object is destructed, calling lua_close on a lua_State in the middle of a Lua callback is obviously very wrong (and is already prevented).

    At this point, a noncopyable basic_state class seems the best solution, but i do not think i have explored every possibility just yet.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C++ template method inlining
    By TotalTurd in forum C++ Programming
    Replies: 3
    Last Post: 08-03-2011, 02:39 AM
  2. Replies: 2
    Last Post: 08-18-2010, 03:24 PM
  3. template method
    By CodeMonkey in forum C++ Programming
    Replies: 4
    Last Post: 03-27-2009, 09:17 PM
  4. template method
    By linuxdude in forum C++ Programming
    Replies: 5
    Last Post: 06-16-2008, 12:32 PM
  5. Why can't it recognize template method?
    By 6tr6tr in forum C++ Programming
    Replies: 9
    Last Post: 04-12-2008, 10:34 AM