C++ Template Parameter automatically with Class Name

I have a triple hierarchy class:

1
2
3
4
5
template<class T> class Singleton;

class Base;

class Sub : public Base, public Singleton<Sub>;


I' using underlying auto pointers, that's why Singleton is a template class and Sub passes itself as a template parameter. I'm developing Singleton and Base and a public API allows anyone to add their own sub classes. I actually want a real triple hierarchy like this:

1
2
3
4
5
template<class T> class Singleton;

class Base : public Singleton<Base>;

class Sub : public Base;


So that external developers don't have to worry about templates and complexity. The problem with this is that my implementation in Singleton will now call the constructor of Base whenever I create an instance of Sub (since the template parameter is Base).

I was wondering if this could be done by pre-processor macros:

1
2
3
4
5
template<class T> class Singleton;

class Base : public Singleton<__CLASS_NAME__>;

class Sub : public Base;


Where __CLASS_NAME__ is the class name that will be replaced by the pre-processor. Theoretically this should be possible, since the __PRETTY_FUNCTION__ macro actually returns the class name. The problem is that one cannot do string-manipulation to remove the function name from __PRETTY_FUNCTION__.

Any ideas on how I can accomplish this so that the Sub class is not aware of inheriting from a Singleton<template> class?
Macros will not do what you want.

The only way to do this that I can think of would be to also make Base a template:

1
2
3
4
5
template <typename T> class Singleton;

template <typename T> class Base : public Singleton<T>

class Sub : public Base<Sub>;


However this is clearly not ideal because it likely destroys the desired inheritance model of having all classes derived from a common base.



The best way to do this, IMO, is just to have sub derive from Singleton directly (per your first example). Or... better yet... don't use singletons.



EDIT:

Just thought of a way to retain a common base:

1
2
3
4
5
6
7
8
9
template <typename T> class Singleton;

// the common base
class Base;

// rename this to something more appropriate
template <typename T> class DeriveFromMe : public Base, public Singleton<T>;

class Sub : public DeriveFromMe<Sub>;

Last edited on
I've thought of that too, but then we have a polymorphism problem:

MyPointer<Base> pointer = MyPointer<Sub>();

Hence you won't be able to use the classes polymorphically when using them as template parameters. If you create and external library, the following will also not work:

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifdef __cplusplus
extern "C"
{
#endif

MyPointer<Base> create()
{
   return Sub::instance();
}

#ifdef __cplusplus
}
#endif 


instance() is a function from Singleton. The pre-processor will now complain: could not convert MyPointer<Sub> to MyPointer<Base>. That's why I wanted the middle class (Base) to NOT be a template class.
I've thought of that too, but then we have a polymorphism problem:


This would work fine with my edited solution (the "DeriveFromMe" example).


Also you can't put C++ code in an extern "C" block. The whole point is that the code inside that block is C code, not C++. So no classes, no polymorphism, no scope operator.... none of that can go in there.
@Disch what? I thought extern "C" just forced the linker to export names as C and not use C++ name mangling. C++ code is used in extern "C" all the time.
Last edited on
I'm also using C++ code in extern "C" all the time. Works perfectly. How would you otherwise create an object in the library that is dynamically loaded during run-time?
Just for reference, these two notations are the same:
1
2
3
extern "C" void f()
{
}
1
2
3
4
5
6
extern "C"
{
    void f()
    {
    }
}
Sometimes I see people who think they're different.
Am I mistaken?


I understand that extern "C" prevents name mangling... but C++ doesn't just mangle names for the hell of it, it does it because it has to in order to allow for things like function overloading.

For example a C++ program can have any number of functions named "foo":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class A
{
  void foo();
};

class B
{
  void foo();
};

namespace n
{
  void foo();
}

void foo();

void foo(int);

void foo(std::string);

// ... etc 


These are all distinctly different functions and therefore must all be exported under different names. They obviously cannot all be exported as "foo". As a result their names have to be mangled to retain scope/type information.

And how would you export something like an overloaded operator without mangling?

 
std::ostream& operator << (std::ostream&, const SomeType&);



The whole point of preventing name mangling (I thought) was to have C++ compiled code able to link to C libraries (hence the name 'extern "C"' and not 'extern "nomangle"')


If you're not going to put C code in the extern C block... then what's the point?



goocreations wrote:
How would you otherwise create an object in the library that is dynamically loaded during run-time?


You'd do it the same way you're doing it now... you'd just remove the extern "C" part. C++ names can still be externally linked, their names will just be mangled.


L B wrote:
C++ code is used in extern "C" all the time.


Can you give me an example of a library that does this? I have never seen it.
Last edited on
Ah, I think we had a misunderstanding. You can put C++ code in an extern "C", but the declarations must be C-compatible, and the implementation can use C++ code. I thought you were saying the implementation had to be C as well.
Last edited on
> You can put C++ code in an extern "C", but the declarations must be C-compatible ...

extern "C" is just a linkage specification; the programming language is still C++, not C.

C linkage imposes some restrictions: a name with C linkage at namespace scope appears at the global scope, only one of a set of overloaded functions can have C linkage, member functions of classes and pointers to members of classes default to C++ linkage, etc.

If a declaration of an entity with C linkage uses types that are not C compatible, we would still be able to use it from C++. In fact, the giving C linkage to functions that are callable from only C++ is a fairly common while exporting symbols from shared objects. When we want to use dlopen(), dlsym(), and dlclose() or their Windows equivalents LoadLibrary(), GetProcAddress() and FreeLibrary() and we do not want to be bothered with decorated C++ names.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>

extern "C" int foo( int& a, int&& b , int c = 0 )
{ std::cout << R"(extern "C" int foo( int&, int&&, int))" << '\n' ; return a += b+c ; }

namespace one
{
    extern "C" void bar(int) { std::cout << R"(extern "C" void one::bar(int))" << '\n' ; }

    extern "C++" void bar(double)
    { std::cout << R"(extern "C++" void one::bar(double))" << '\n' ; }
}

extern "C" void bar(int) ; // one::bar

extern "C"
{
    struct A
    {
        int v = 7 ;
        int foo() const { return v ; } // A::foo is extern "C++"
    };

    void (*pfn1)(int) ; // pfn1 is extern "C"

    extern "C++" { void (*pfn2)(int) ; }

    int fun( const A* p ) // fun is extern "C"
    {
        pfn1 = &one::bar ;
        auto f1 = *pfn1 ; // f1 has "C" linkage
        f1(20) ;

        pfn2 = &one::bar ;
        auto f2 = *pfn2 ;
        f2(20) ;

        return p->foo() ;
    }

}

int main()
{
    int i = 7 ;
    float f = 0.7f ;

    foo( i, +i ) ; // extern "C" int& foo( int&, int&&, int)
    ::bar(i) ; // extern "C" void one::bar(int)
    one::bar(i) ; // extern "C" void one::bar(int)
    one::bar(f) ; // extern "C++" void one::bar(double)

    A a ;
    std::cout << fun(&a) << '\n' ; // 7
}


Snippet: http://liveworkspace.org/code/2YODvX$0



> I'm also using C++ code in extern "C" all the time. Works perfectly.

Yes.

> How would you otherwise create an object in the library that is dynamically loaded during run-time?

You would need to use the C++ name (with would be a decorated name)
Last edited on
Topic archived. No new replies allowed.