Template class that has all inline virtual functions

I am using class templates that contain virtual functions in my current project, and I came across a problem that I cannot overcome on my own.

  • Class templates cannot share the bodies of the member members of the class definition in the .hpp file due to linker errors. I don’t want to create templates for every new type that I use, so all leave them nested. This is absolutely excellent, as they make up 1-2 lines in most of the time, so I am not going to try out any bloated code.
  • On the other hand, gcc creates a vtable for a polymorphic class in a .cpp file with the definition of the first non-built-in function that is declared in the class definition. Since I have all the members of the inline function, I get an undefined reference to the vtable, or not, the RTTI character found for my class in GDB.

Pay attention to the following code:

template <typename T> struct Test { virtual void testMe() const = 0; virtual ~Test() = default; }; template <typename T> struct test : public Test<T> { virtual void testMe() const { std::cout << typeid(T).name() << std::endl; } virtual ~test() = default; }; int main() { test<int> t; Test<int>& T = t; T.testMe(); return 0; } 

In this particular example, I get:

 can't find linker symbol for virtual table for `test<int>' value 

when debugging using GDB.

How to get my compiler to put vtable in a specific cpp file when all class functions are built-in?


EDIT

Since the above example does not illustrate the problem, here is my original code.

The class causing the problem:

 #ifndef CONVERTIBLETO_H #define CONVERTIBLETO_H #include "convertibleTo_converters.h" #include <functional> template < typename IT, template <typename InterfaceType, typename ErasedType> class Converter = convertibleTo_detail::default_converter > class convertibleTo { public: typedef convertibleTo<IT, Converter> this_type; typedef IT InterfaceType; struct is_type_eraser_tag {}; private: class holder_interface { public: virtual InterfaceType get() const = 0; virtual void set(const InterfaceType&) = 0; virtual holder_interface* clone() const = 0; virtual ~holder_interface() {} }; template <typename ErasedType> class holder : public holder_interface { public: virtual InterfaceType get() const { return (Converter<InterfaceType, ErasedType>::convert(this->data)); } virtual void set(const InterfaceType& value) { this->data = (Converter<InterfaceType, ErasedType>::convert(value)); } virtual holder_interface* clone() const { return new holder(*this); } holder() = delete; holder(const holder& other): data(other.data) { } holder(ErasedType& d): data(d) { } virtual ~holder() = default; private: ErasedType& data; }; public: inline InterfaceType get() const { if (this->held) return this->held->get(); else return InterfaceType(); } inline void set(const InterfaceType& value) { if (this->held) this->held->set(value); } inline bool empty() const { return ! this->held; } convertibleTo<InterfaceType, Converter>& operator= (const convertibleTo<InterfaceType, Converter>& other) { if(this->held) delete this->held; this->held = other.held->clone(); return *this; } convertibleTo(): held(nullptr) { } template <typename T> explicit convertibleTo(T& data): held(new holder<T>(data)) { } convertibleTo( convertibleTo& other ): convertibleTo( const_cast<const convertibleTo&>(other)) { } convertibleTo( const convertibleTo& other ): held(nullptr) { if(other.held) this->held = other.held->clone(); } ~convertibleTo() { if (this->held) delete this->held; } private: holder_interface * held; }; #endif 

Necessary helper classes:

 #ifndef CONVERTIBLETO_CONVERTERS_H #define CONVERTIBLETO_CONVERTERS_H #include <string> #include <sstream> namespace convertibleTo_detail { template <typename InterfaceType, typename ErasedType> struct default_converter { static inline InterfaceType convert(const ErasedType& input) { return input; } static inline ErasedType convert(const InterfaceType& input) { return input; } }; template <typename T> struct default_converter<T, T> { static inline T convert(const T& input) { return input; } }; template <typename ErasedType> struct default_converter<std::string, ErasedType> { static inline std::string convert(const ErasedType& input) { default_converter<std::string, ErasedType>::prepareConverter(); default_converter<std::string, ErasedType>::converter << input; return default_converter<std::string, ErasedType>::converter.str(); } static inline ErasedType convert(const std::string& input) { default_converter<std::string, ErasedType>::prepareConverter(input); ErasedType result; default_converter<std::string, ErasedType>::converter >> result; return result; } private: static std::stringstream converter; struct SetExceptionFlagsOnce { SetExceptionFlagsOnce() { default_converter<std::string, ErasedType>::converter.exceptions(std::stringstream::failbit); } }; static void inline prepareConverter(std::string value = "") { static SetExceptionFlagsOnce setter; default_converter<std::string, ErasedType>::converter.clear(); default_converter<std::string, ErasedType>::converter.str(value); } }; template <typename ErasedType> std::stringstream default_converter<std::string, ErasedType>::converter; template <> struct default_converter<std::string, std::string> { static inline std::string convert(const std::string& input) { return input; } }; } #endif // CONVERTIBLETO_CONVERTERS_H 

main.cpp:

 #include <iostream> #include "convertibleTo.h" int main() { int I = 5; convertibleTo< std::string > i(I); std::cout << i.get() << std::endl; i.set("321"); std::cout << i.get() << std::endl; return 0; } 

The error I get is:

 RTTI symbol not found for class 'convertibleTo<std::string, convertibleTo_detail::default_converter>::holder<int>' 

it displays when I go inside i.get () and then inside the get () holder.


EDIT . Relocated full source from pastebin here, as per suggestion


Since the last two comments have suggested that this is a GDB bug, how can I check this next time?

  • If GDB complains about the lack of vtable - would you confirm that I can access each virtual member through a link to the ABC initialized by the derived class, enough to confirm that everything is in order?
  • If GDB complains about the absence of an RTTI character, will typeid () invoke to refer to an ABC initialized by a derived class sufficient to confirm that the RTTI character is in fact present?
+7
c ++ templates member inline gdb
source share
1 answer

Your code (the full version with two header files and main.C) compiles and links without any errors for me, with gcc 4.8.3, with default parameters (except for -std = C ++ 11, to enable C + mode + 11).

I even uploaded the resulting executable to gdb. gdb swallowed it without any problems.

I don’t see anything bad here.

0
source share

All Articles