Help with my macro

Sometimes I feel that the C preprocessor is worse than merely fundamental/simplistic but down-right crippled. Anyway...

So I have this macro that defines a bunch of functions based around creating a window 'object'. It should also allow me to create extra data for the struct that is associated with the object. The struct contains a window handle member, always, so I want it to define that every time without me having to; each struct instance will have a unique window handle. Conversely the Window's extended data (with SetClassLong) will have a pointer to the struct instance.

I was hoping to have two macros working as a pair, as follows:

DEFINE_WINDOW_OBJECT(OBJECT_NAME, WINDOW_NAME, WIN_STYLES, WINEXT_STYLES, WCLASS_STYLES)
/* body of a struct definition */
END_DEFINE_WINDOW_OBJECT

However, this seems impossible.

First, the struct definition has to pre-exist any usage of it, and a lot of the definition code in the macro references it. I want to only specify the arguments once; yet the arguments are common to both macros. I tried nesting defines inside a define in my latest attempt, only to discover that the preprocessor strips out all whitespace yet preprocessor operators rely on whitespace for termination. I don't know if this is arrogance of the spec creators for not being able to foresee any use for it themselves, or if there's a much better reason.

Here is the current (non-working) attempt:

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
56
57
58
59
#ifndef __WINOBJ_H__
#define __WINOBJ_H__
#include "error.h"
#include <windows.h>
#include <assert.h>

#define END_DEFINE_WINDOW_OBJECT \
HWND win_handle;\
};\
WNDCLASSA class_##OBJECT_NAME## = {0};\
long proc_##OBJECT_NAME##(HWND window, unsigned int msg, WPARAM par1, LPARAM par2);\
extern CREATESTRUCTA *pre_make();\
extern struct OBJECT_NAME##_s *make_##OBJECT_NAME##_s();\
extern void pre_destroy();\
HWND parent, menu;\
const char *wndclass_name = #OBJECT_NAME;\
void initclass_##OBJECT_NAME##() {\
  static init = 0;\
  assert(init != 1);\
  class_##OBJECT_NAME##.style = WCLASS_STYLES##;\
  class_##OBJECT_NAME##.lpfnWndProc = (WNDPROC)proc_##OBJECT_NAME##;\
  class_##OBJECT_NAME##.cbClsExtra = 0;\
  class_##OBJECT_NAME##.cbWndExtra = 4;\
  class_##OBJECT_NAME##.hCursor = LoadCursorA(NULL, MAKEINTRESOURCEA(IDC_ARROW));\
  class_##OBJECT_NAME##.hbrBackground = NULL;\
  class_##OBJECT_NAME##.lpszClassName = wndclass_name;\
  printf("wndclass_name: %s\n", wndclass_name);\
  if(RegisterClassA(&class_##OBJECT_NAME##) == 0)\
  ErrorExit();\
  init = 1;\
}\
HWND get_parent() {\
  return parent;\
}\
HWND get_menu() {\
  return menu;\
}\
struct OBJECT_NAME##_s *make_##OBJECT_NAME##_s() {\
  struct OBJECT_NAME##_s *t;\
  CREATESTRUCTA cr, *cr2;\
  t = malloc(sizeof(struct OBJECT_NAME##_s##));\
  cr2 = pre_make(&cr, t);\
  SetClassLongA(t->win_handle = CreateWindowExA(WINEXT_STYLES, wndclass_name, WINDOW_NAME, WIN_STYLES,\
  0, 0, 0, 0, NULL, NULL, get_app_handle(), cr2), 0, (LONG)t);\
  return t;\
}\
struct OBJECT_NAME##_s destroy_##OBJECT_NAME##_s(struct OBJECT_NAME##_s *t) {\
  pre_destroy(t);\
  DestroyWindow(t->win_handle);\
  free(t);\
}

#define DEFINE_WINDOW_OBJECT(_OBJECT_NAME, _WINDOW_NAME, _WIN_STYLES, _WINEXT_STYLES, _WCLASS_STYLES) \
#define OBJECT_NAME _OBJECT_NAME\
#define WINDOW_NAME _WINDOW_NAME\
#define WIN_STYLES _WIN_STYLES\
#define WCLASS_STYLES _WCLASS_STYLES\
struct OBJECT_NAME##_s {
#endif 


Having the whitespace stripped out prevents this from working. I'm sure my foggy brain is preventing me from seeing a simple solution...
Last edited on
Ask yourself the enlightening question: are you making your work easier by going down this road? (Even if it worked?)

As for stripping whitespaces, have you tried putting /**/ comments where you need the whitespaces?
You don't seem to understand why stripping whitespace is an issue. Definitions are newline-terminated. A comment isn't a newline and would be a part of the definition.
ATL uses known typedef's to allow the END_ macro to use types passed to the BEGIN_ macro. This only works inside a class, though because it is the same typedef name everywhere. I guess it works inside namespaces too.

1
2
3
4
5
6
7
8
9
10
11
12
13
#define BEGIN_SOMETHING(name) namespace ns_##name## \
{\
    typedef struct {\
        HWND hWnd;\
        LPVOID extra;\
    } _MyName;\


//Now the END_ macro can use _MyName and close the closing brace for the namespace.

#define END_SOMETHING \
    _MyName myVariable = { 0 };\
} 

I'm struggling to see how this would help...

EDIT: I was stupid enough to ask this question on a C++ forum when I'm actually porting an app from C++ to C. *sigh* I am trying to avoid code duplication and OO concepts were intended for that purpose. I'm not sure if there is any workaround.

EDIT: Right now I have this as the opening macro
1
2
3
4
5
6
#define DEFINE_WINDOW_OBJECT(_OBJECT_NAME, _WINDOW_TITLE, _WIN_STYLES, _WINEXT_STYLES, _WCLASS_STYLES)
const char window_title [] = _WINDOW_TITLE;\
const char wclass_name [] = #_OBJECT_NAME;\
const int win_styles = _WIN_STYLES, winext_styles = _WINEXT_STYLES, wclass_styles = _WCLASS_STYLES;\
typedef struct _OBJECT_NAME {
#endif 


Closing the typedef in the subsequent macro (with } _s;) allows me to use the struct in the subsequent macro. However... one thing I can't do is pass across identifiers as if they were constants (which a define would permit). So I can't have a function called make##_OBJECT_NAME in the subsequent macro.

EDIT: Decided to go with terminating a macro with END_DEFINE_WINDOW_OBJECT(_OBJECT_NAME). Have to repeat the name twice but it'll have to do.
Last edited on
Topic archived. No new replies allowed.