views:

903

answers:

4

UPDATE: Obviously, you'd want to do this using templates or a base class rather than macros. Unfortunately for various reasons I can't use templates, or a base class.


At the moment I am using a macro to define a bunch of fields and methods on various classes, like this:

class Example
{
  // Use FIELDS_AND_METHODS macro to define some methods and fields
  FIELDS_AND_METHODS(Example)
};

FIELDS_AND_METHODS is a multi-line macro that uses stringizing and token-pasting operators.

I would like to replace this with the following kind of thing

class Example
{
  // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol
  // defined, to achieve the same result as the macro.
  #define TYPE_NAME Example
  #include "FieldsNMethods.h"
};

Here I #define the name of the class (previously the parameter to the macro), and the FieldsNMethods.h file contains the content of the original macro. However, because I'm #including I can step into the code at runtime, when debugging.

However I am having trouble 'stringizing' and 'token pasting' the TYPE_NAME preprocessor symbol in the FieldsNMethods.h file.

For example, I want to define the destructor of the class in FieldsNMethods.h, so this would need to use the value of TYPE_NAME as below:

~TYPE_NAME()
{
  //...
}

But with TYPE_NAME replaced by its value.

Is what I'm attempting possible? I can't use the stringizing and token-pasting operators directly, because I'm not in a macro definition.

+5  A: 

This cries out for a template.

class Example<class T>
{
    ...class definition...
};

The direct answer to the last part of your question - "given that I'm not in a macro definition any more, how do I get pasting and stringizing operators to work" - is "You can't". Those operators only work in macros, so you'd have to write macro invocations in order to get them to work.

Added:

@mackenir said "templates are not an option". Why are templates not an option? The code is simulating templates the old-fashioned pre-standard, pre-template way, and does so causing much pain and grief. Using templates would avoid that pain -- though there'd be a conversion operation.

@mackenir asked "is there a way to make things work with macros?" Yes, you can, but you should use templates - they are more reliable and maintainable. To make it work with macros, then you'd have to have the function names in the code in the included header be macro invocations. You need to go through a level of indirection to get this to work correctly:

#define PASTE_NAME(x, y) PASTE_TOKENS(x, y)
#define PASTE_TOKENS(x, y) x ## y

#define TYPE_NAME Example
int PASTE_NAME(TYPE_NAME, _function_suffix)(void) { ... }

This level of indirection is an often necessary idiom for both tokenizing and stringizing operators.


Additional comments from @mackenir indicate continued problems. Let's make it concrete.

At the moment I am using a macro to define a bunch of fields and methods on various classes, like this:

class Example
{
    // Use FIELDS_AND_METHODS macro to define some methods and fields
    FIELDS_AND_METHODS(Example)
};

FIELDS_AND_METHODS is a multi-line macro that uses stringizing and token-pasting operators.

I would like to replace this with the following kind of thing

class Example
{
    // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol
    // defined, to achieve the same result as the macro.
    #define TYPE_NAME Example
    #include "FieldsNMethods.h"
};

OK. To make this concrete, we need a FIELDS_AND_METHODS(type) macro that is multi-line and uses token-pasting (I'm not going to deal with stringizing - the same basic mechanisms will apply, though).

#define FIELDS_AND_METHODS(type) \
    type *next; \
    type() : next(0) { } \
    type * type ## _next() { return next; }

With luck, this declares a member of the type 'pointer to argument type', a constructor for that type, and a method (Example_next in this case) that returns that pointer.

So, this might be the macro - and we need to replace it such that the '#include' does the equivalent job.

The content of fieldsNmethods.h becomes:

#ifndef TYPE_NAME
#error TYPE_NAME not defined
#endif
#define FNM_PASTE_NAME(x, y)    FNM_PASTE_TOKENS(x, y)
#define FNM_PASTE_TOKENS(x, y)  x ## y

TYPE_NAME *next;
TYPE_NAME() : next(0) { }
TYPE_NAME * FNM_PASTE_NAME(TYPE_NAME, _next)() { return next; }

#undef FNM_PASTE_NAME
#undef FNM_PASTE_TOKENS

Note that the header would not contain multiple-inclusion guards; its raison d'etre is to allow it to be included multiple times. It also undefines its helper macros to permit multiple inclusion (well, since the redefinitions would be identical, they're 'benign' and wouldn't cause an error), and I prefixed them with FNM_ as a primitive namespace control on the macros. This generates the code I'd expect from the C pre-processor. and G++ doesn't witter but produces an empty object file (because the types declared are not used in my example code).

Note that this does not require any changes to the calling code except the one outlined in the question. I think the question should be improved using the SPOT "Single Point of Truth" principle (or DRY "Don't Repeat Yourself"):

#define TYPE_NAME Example
class TYPE_NAME
{
    // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol
    // defined, to achieve the same result as the macro.
    #include "FieldsNMethods.h"
};
Jonathan Leffler
Thanks. Templating isnt an option. So, are you implying that there's a way to achieve the same effect as the stringizing and token-pasting operators in FieldsNMethods.h?
mackenir
The classes are public facing API and we don't want to pollute them by making the templates, or having a base-class. Also, they are C++/CLI classes so private inheritance isn't an option. Thanks for the further info, I'll try that out.
mackenir
'pollute' I mean 'expose implementation details in a way that might confuse API users'.
mackenir
and you use macros for that? that sounds evil! Can't you write a kind if facade to expose only what you want to the client and internally have sth maintainable?
Nazgob
We don't want to introduce many levels of indirection, and we'd still need macros to pass-through to the internal implementation (or otherwise have to write lots of the same code many times).
mackenir
A: 

No, you can't define class or function definitions on the fly. They have to be specified, either by typing them in directly or by defining them in the preprocessor.

Typically, there is no need to generate classes like this, and the class definitions are created before compilation, whether by typing everything in or using some sort of code generation. Sometimes there is a separate code generation step (in current Visual Studio, for example, you can define pre- and post-processing steps).

Now, if you needed to create different versions of some classes for different data types, you'd use templates. You can't create rubber-stamp classes of differing names that way.

One last question: why are you doing this? I've never been in a position where something like that would look useful in C++, and in languages where this does make sense there are facilities to do it.

David Thornley
What Im trying to do is effectively 'parameterize' a #include in the same way as a macro can be parameterized. So there's no on-the-fly generation going on. It's all at (actually, before) compile time.
mackenir
+2  A: 

You should wrap the stringifying with another macro (2 are necessary because of how the preprocessor works)

In FieldsNMethods.h

#define MAKE_STR_X( _v ) # _v
#define MAKE_STR( _v ) MAKE_STR_X( _v )

char *method() { return MAKE_STR( TYPE_NAME ); }
epatel
Thanks. That sounds promising - I'll try that.
mackenir
+3  A: 

You have to add an extra layer of macros:

#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x

#define TOKENPASTE(x, y) TOKENPASTE2(x, y)
#define TOKENPASTE2(x, y) x ## y

The reason is that when you have a macro, the preprocessor normally recursively expands the arguments before performing the macro substitution. However, if any argument is used with the stringizing operator # or the token-pasting operator ##, it is not expanded. Therefore, you need an extra layer of macros, where the first layer expands the arguments, and the second layer performs the stringizing or token pasting.

If the arguments need to be expanded multiple times (such as #define A B, #define B C, #define C D, STRINGIZE(A)), then you need to add that many more layers before you apply the # or ## operators.

Adam Rosenfield