3

I recently acquired some code that I want to port to Linux. In a header file though, I have some curious code that I hope someone can shed some light on. Within a header file, inside a namespace where other classes are defined, I have the following:

#define CREATE_SINGLETON_METHODS(s) \
  private: \
    friend class Singleton<c>; \
    ##c(); \
    virtual ~##c();

I understand that the ## is the token pasting operation, but I can't figure out why the original author (who I don't know, and can't contact) used it. I have an implementation class that looks like this:

class MapManager : public Singleton<MapManager> {
CREATE_SINGLETON_METHODS(MapManager)

private:
...

When I compile, I get the following error:

error: pasting ";" and "MapManager" does not give a valid preprocessing token

This compiles find on Windows and some earlier versions of gcc (pre 4.x). Any ideas as to what could be going on here? Thanks!

8 Answers8

2

I use my own generic singleton template: Try replacing those icky macros with this template:

#ifndef SINGLETON_HPP_STYLET
#define SINGLETON_HPP_STYLET 0


/*
*
*
*   Generic Singleton implementation
*
*
*/ 


        template <class T>
        class SingletonHolder : public T
        {
            public:                                  
                static SingletonHolder<T>& getInstance()
                {

                    static SingletonHolder<T> instance;
                    return instance;
                }

            private:
                SingletonHolder()
                {
                };

                virtual ~SingletonHolder()
                {
                };

        };//class SingletonHolder



#endif //SINGLETON_HPP_STYLET

//------------------------------------------------------------------

USAGE:

class SomeClass;

typedef SingletonHolder<SomeClass> SomeClassSingleton;


SomeClassSingleton::getInstance().doSomething();
dempl_dempl
  • 123
  • 1
  • 8
1

Looks to me like the ## before the constructor isn't useful. The one on the destructor is meant to paste the tilde to the typename. Perhaps the original author copy-and-pasted, and didn't get a compile error, because it is an older version, so didn't catch the error.

Todd Gardner
  • 13,313
  • 39
  • 51
1

Try changing the macro to this:

#define CREATE_SINGLETON_METHODS(c) \
  private: \
    friend class Singleton<c>; \
    c(); // Note the change on this line! \
    virtual ~##c();

I took out one of the ## operators. Looks like ";" was being merged with "c();" in gcc 4.

Naaff
  • 9,213
  • 3
  • 38
  • 43
  • This looks to be just a difference between the preprocessors. – Dolphin Jun 23 '09 at 21:49
  • Right - that's the point. Looks like gcc4's preprocessor is pasting the token "Singleton;" and the expansion of "c();" together when it sees the ## operator. This operator is unnecessary though, so the simple fix is to just remove it. – Naaff Jun 23 '09 at 22:23
  • Token pasting on the destructor is wrong too. The destructor is named using the tokens "~" and then the class name. They do not need to be pasted together, even though they are conventionally written without whitespace between them. – Doug Jun 24 '09 at 11:16
  • @Doug: good point, but I don't think it's "wrong" in the sense that it's causing any problems... just unnecessary. – Naaff Jun 24 '09 at 14:20
1

I always find that this kind of preprocessor "magic" (read garbage) creates more trouble than it's worth. In some cases it's a necessary evil but I don't think that applies here. I'd rewrite any instances of it in the code and eliminate that hideous thing.

markh44
  • 5,804
  • 5
  • 28
  • 33
1

The preprocessor operators for 'stringizing' and token pasting ('#' and '##') can only be reliably used on macro parameters. Also, you generally need to use a level of indirection to get them to work in all cases (particularly when using them with things that are themselves macros).

See

for some additional details.

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
1

I had a similar problem sometime ago. Try running:

g++ -E mytestfile.h

(using your own filename of course) on the command line to see what the results of the preprocessor are. It may help show what is going on. I am running gcc 3.4.4 and get the same errors you describe as happening only in 4.x (though the code works fine in Visual Studio 2008).

Eliminating the tokenizers will fix the problem:

#define CREATE_SINGLETON_METHODS(c) \
private: \
friend class Singleton<c>; \
c(); \
virtual ~c();

This code gives me the following as a preprocessor result (copied from my command line with the -E option):

private: friend class Singleton<MapManager>; MapManager(); virtual ~MapManager();

which the compiler is happy with.

According to http://gcc.gnu.org/onlinedocs/gcc-4.0.4/cpp/Tokenization.html:

Once the input file is broken into tokens, the token boundaries never change, except when the `##' preprocessing operator is used to paste tokens together. The compiler does not re-tokenize the preprocessor's output. Each preprocessing token becomes one compiler token.

'##' breaks the tokenization rules in this case. Per the error your getting, the preprocessor most likely gave the compiler a token set with ";MapManager()" in it, which the 4.x compiler obviously doesn't like.

And some information form here (http://gcc.gnu.org/onlinedocs/cpp/Concatenation.html#Concatenation) is also worth noting:

It is common to find unnecessary uses of '##' in complex macros. If you get this warning, it is likely that you can simply remove the '##'.

Hope that helps.

Adam
  • 433
  • 5
  • 11
0

How on earth did you get this to compile at any compiler? First of all it should probably be:

#define CREATE_SINGLETON_METHODS(c) \
  private: \
    friend class Singleton<c>; \
    ##c(); \
    virtual ~##c();

Since I assume it's the macro parameter that is defining the friend class, a private constructor and a private destructor.

rtn
  • 127,556
  • 20
  • 111
  • 121
  • Yes, that is correct. I had a typo when I entered my question, but everything else is correct. I have no idea how this compiles, but it compiles on Windows (Visual C++ 8.0) without any complaints. –  Jun 23 '09 at 20:47
-1

It looks like a poorly-done attempt at automatic singleton creation. In general this is possible to do without macros if you can put up with a virtual function or virtual inheritance with some clever template tricks.

If I were you, I would just manually rewrite the related code for the classes affected. Macros are bad news for stuff like this.

rlbond
  • 65,341
  • 56
  • 178
  • 228