Thread: Function template specialization and duplicate symbols

  1. #1
    Lode Runner
    Join Date
    May 2004
    Posts
    53

    Function template specialization and duplicate symbols

    Hi,

    There's probably a simple way to get rid of this, but I just can't find it.

    I have a class with two function templates. One of them calls the other, and the other one is defined with specializations.
    The Foo class is defined in 3 files:

    Foo.h
    Code:
    #pragma once
    
    #include <map>
    #include <vector>
    
    
    class Foo
    {
    public:
    	Foo();
    	virtual ~Foo() {}
    	
    	int bar();
    	
    	template <typename T> std::vector<int> enum_keys() const;
    	
    	template <typename T> const std::map<int, T>& get_map() const;
    	
    protected:
    	std::map<int, int>	_int_map;
    	std::map<int, float> _float_map;
    };
    
    #include "Foo.hpp"
    Foo.hpp
    Code:
    template <typename T>
    std::vector<int> Foo::enum_keys () const
    {
    	std::vector<int> keys;
    	
    	const std::map<int, T>& mapref = get_map<T>();
    	
    	typename std::map<int, T>::const_iterator end = mapref.end();
    	typename std::map<int, T>::const_iterator it = mapref.begin();
    	for (; it != end; ++it)
    	{
    		keys.push_back(it->first);
    	}
    	
    	return keys;
    }
    
    template <>
    const std::map<int, int>& Foo::get_map<int>() const
    {
    	return _int_map;
    }
    
    template <>
    const std::map<int, float>& Foo::get_map<float>() const
    {
    	return _float_map;
    }
    Foo.cpp
    Code:
    #include "Foo.h"
    
    Foo::Foo()
    {
    }
    
    int Foo::bar()
    {
    	const std::map<int, float>& floatmap = get_map<float>();
    	return floatmap.begin()->first;
    }
    and here is my main:
    Code:
    #include "Foo.h"
    
    int main(int argc, char** argv)
    {
    	Foo foo;
    	
    	return 0;
    }
    The linker gives me a duplicate symbol error I can't get rid of:
    Code:
    duplicate symbol __gnu_debug_def::map<int, int, std::less<int>, std::allocator<std::pair<int const, int> > >
     const& Foo::get_map<int>() const in Foo.o and main.o
    I believe there is a way to actually tell the compiler which templates to generate, but I haven't found the way yet...
    Anyone can help me on this ?
    Last edited by krappa; 07-29-2009 at 11:38 AM.

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    You may want to use template traits:
    Code:
    template<typename T> class my_class_traits { };
    template<> class my_class_traits<float>
    {
    protected:
        std::map<int, flat> m_Map;
    };
    // Repeat for int
    
    template<typename T> class myclass: protected my_class_traits<T>
    {
        // Use m_Map to access your map now.
    }
    No duplicate maps. No need for an access method, either.
    Simple, easy and efficient.
    Last edited by Elysia; 07-29-2009 at 11:09 AM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #3
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    Quote Originally Posted by Elysia View Post
    You may want to use template traits:
    Code:
    template<typename T> class my_class_traits { };
    template<> class my_class_traits<float>
    {
        std::map<int, flat> m_Map;
    };
    // Repeat for int
    
    template<typename T> class myclass: protected my_class_traits<T>
    {
        // Use m_Map to access your map now.
    }
    No duplicate maps. No need for an access method, either.
    Simple, easy and efficient.
    I don't quite get where you would want me to use "myclass". As a member of Foo, or as replacement for Foo?

    Case 1: Is that going to change anything, since map is already a template class? Isn't this just adding a layer?

    Case 2: I need Foo/myclass to contain all the maps. I can instantiate only one Foo/myclass.

    Is there another solution, without breaking my architecture too much?

  4. #4
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by krappa View Post
    I don't quite get where you would want me to use "myclass". As a member of Foo, or as replacement for Foo?
    It is your Foo. Myclass is basically your main class and the traits is the things you want to specialize depending on the type.

    Case 1: Is that going to change anything, since map is already a template class? Isn't this just adding a layer?
    Yes.
    It does involve an extra, but again, you don't have TWO or MORE maps for every possible type NOR do you need your accessor function get_map, which you also specialize. So all in all, it's more efficient (less memory) and less typing in the end.

    Case 2: I need Foo/myclass to contain all the maps. I can instantiate only one Foo/myclass.
    But it's inheriting from the traits class, so your main class gets the appropriate map!

    Is there another solution, without breaking my architecture too much?
    This is the standard way to do things like these. It shouldn't break your architecture much. All you need to do is add the traits, and let the get_map function just return the generic map members that you inherit. Then you can remove the specialized functions, too.

    But as for why you get a linker error, I can't say.
    Although I do notice that your definition of the get_map function is wrong.
    It should be:
    Code:
    template<typename T> template <>
    const std::map<int, int>& Foo<T>::get_map<int>() const
    The same for the rest.
    Last edited by Elysia; 07-29-2009 at 11:12 AM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  5. #5
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    Quote Originally Posted by Elysia View Post
    But as for why you get a linker error, I can't say.
    Although I do notice that your definition of the get_map function is wrong.
    It should be:
    Code:
    template<typename T> template <>
    const std::map<int, int>& Foo<T>::get_map<int>() const
    The same for the rest.
    Hmm, I still don't get it.
    Foo isn't a class template, it's a normal class with two function templates.

    Using your solution above, I'm going to have to instantiate Foo/Myclass specifying the type.
    For example:
    Code:
    Foo<int> foo;
    But in that case, foo is only going to contain one map, the map<int, int> m_Map it inherited. Right?

    That's not what I want, I want foo to have all maps at all times. Think of Foo as an "address book" that maps different types. The list of maps can be arbitrary long, and I want a map for each type I decide.
    In this example, bar() is the only method, but in my real-life case, I have loads of methods, all of them using enum_keys and specifying which template to use (int, float, object pointers...).

    Either your solution is not what I'm trying to do, or I just don't get it. (example using Foo, please?)
    Last edited by krappa; 07-29-2009 at 11:27 AM.

  6. #6
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Right. I mis-read the code and misread the purpose of the code. Sorry about that >_<
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by krappa
    I believe there is a way to actually tell the compiler which templates to generate, but I haven't found the way yet...
    Move the definitions of those function template specialisations into Foo.cpp, and forward declare them in Foo.hpp:
    Code:
    template <>
    const std::map<int, int>& Foo::get_map<int>() const;
    
    template <>
    const std::map<int, float>& Foo::get_map<float>() const;
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Lode Runner
    Join Date
    May 2004
    Posts
    53
    Quote Originally Posted by laserlight View Post
    Move the definitions of those function template specialisations into Foo.cpp, and forward declare them in Foo.hpp:
    Code:
    template <>
    const std::map<int, int>& Foo::get_map<int>() const;
    
    template <>
    const std::map<int, float>& Foo::get_map<float>() const;
    Great! That's the thing I was looking for.
    Works like a charm.

    Thanks laserlight.

Popular pages Recent additions subscribe to a feed