Macro to initialize a struct

I need to initialize a structure using macros. Below is the structure and the macros:
struct Person
{
     int age;
     char *pName;
};

//some macs here
#define BEGIN_INIT_PERSON(VarName) \
     Person VarName = { 0,
     
#define AGE(val)    \
          val,
         
#define NAME(nam)    \
          nam

#define END_INIT_PERSON()   }; \
 
Actually, I want the macros to be more robust in the sense that if someone misses a struct field initialization, then a compiler error should be generated.
e.g., the following usage should generate a compiler error
BEGIN_INIT_PERSON(p1)
AGE(2)
END_INIT_PERSON()
in this case, the name will be assigned a garbage value.
     I tried a couple of things such as nested macros etc. But I concluded that somehow I should make the AGE and NAME macros define another macro based on VarName so that I can check it in END_INIT_PERSON so that I can generate an error using #error directive. But could  not actually get it work.
Any ideas greatly appreciated.
Thanks.
LVL 2
vbk_bgmAsked:
Who is Participating?
 
jcgdConnect With a Mentor Commented:
You must use the four macro to declare a complete declaration and initialization struct:
BEGIN_INIT_NAME : declare a variable of typePerson
AGE: open a bracket an assign a value to age
NAME: assign a value to name and close bracket
END_INIT_PERSON(): put end of sentence ;

0
 
jkrCommented:
Why don't you use a constructor to initialize your struct, e.g.

struct Person
{
    Person ( int _age, char* _pName) {
     age = _age; pName = _pName;
    }
    int age;
    char *pName;
};

By not providing a default ctor (i.e. a ctor that takes no arguments), you'll make providing the init values mandantory...
0
 
jasonclarkeCommented:
I agree...this is a horrible idea...

use a constructor, it is there for this very reason...
0
Cloud Class® Course: Certified Penetration Testing

This CPTE Certified Penetration Testing Engineer course covers everything you need to know about becoming a Certified Penetration Testing Engineer. Career Path: Professional roles include Ethical Hackers, Security Consultants, System Administrators, and Chief Security Officers.

 
jkrCommented:
>>I agree...this is a horrible idea...

Would make sense for plain C, but in C++ (as you hit the nail on the head): 'use a constructor, it is there for this very reason'
0
 
vbk_bgmAuthor Commented:
It's all legacy code. The macros are ther to abstract the user from the implementation details and hence provide an easy-to-use way to init the structure. the structure has many fields. Similar macros are also used in MFC (message maps).
0
 
AssafLavieCommented:
Why not initialize using an aggregate initializer?

struct c{ int a, b; };

c c1 = { 1, 2 };

0
 
jasonclarkeCommented:
So, is it C or C++.  If it is C++, there is no excuse.

> Similar macros are also used in MFC (message maps).

That doesn't make it the right thing to do.  I doubt there is anyone out there who thinks MFC is a model piece of C++ code.
0
 
AssafLavieCommented:
I agree. These macro maps are almost always a sign of poor design.
0
 
jtwine100697Commented:
> Why not initialize using an aggregate initializer

Actually, that is what the macros are building...

-=- James.
0
 
BlackDiamondCommented:
If this is really C++, you should do something like the following.  If you really still need to use the struct, just use the methods below to modify a struct instead of using the local variables.  C++ is very well suited to what you want to do (protect the implementers from themselves), but you will probably need to do a little more work up front to accomplish this, and make your legacy code compatible.  Keep in mind that macros are very prone to cause complier errors in places that you don't expect (which can be very hard to find), and are bad practice in general.  If you need Macro like definitions, try using the const keyword instead.


class Person{
   Person(){}

   void setAge(int age) {
      this->age = age;
   }
   int getAge() {
      return age;
   }
   int setName(char * pName) {
      /* Make sure the memory is allocated correctly */
      if (!this->pName || strlen(this->pName) != strlen(pName))
         if (!realloc(this->pName, strlen(pName) + 1))
            return 0;
      strncpy (this->pName, pName, strlen(pName) + 1);
      return 1;
   }
   char * getName() {
      return pName;
   }

   int isValid() {
      if ((age && pName) && (strlen(pName) > 0))
         return 1;
      else
         return 0;
   }
   private:
   int age;
   char *pName;
}

....

Person p = new Person();

p.setAge(10);
if (!p.setName("myname")) throw new whateverException();
if (!p.isvalid) throw new whateverException();
0
 
AssafLavieCommented:
I don't understand what you wish to gain by using macro's to wrap the aggregate initializer.
If your goal is to allow programmers to initialize only part of the struct (or something along that line) why not use an initializer function?
Most compilers will inline this sort of routine and some of them will even do NRV optimiztion so there won't be any cost.
0
 
jtwine100697Commented:
BlackDiamond, generally, people name the parameters differently in order to prevent clashs, and do not use "this" directly.

Also, that example:
   o does not degrade gracefully when ::realloc(...) fails
   o does not free allocated memory
   o should have getName(...) return a const pointer

-And some think that malloc/realloc/free have no real place in C++ development (except in very specific situations).  

IOW, do *not* use that code as-is in *any* project!

-=- James.
0
 
BlackDiamondCommented:
jt,
I realize that is not good code.  I wrote it in about 3 minutes off the top of my head.  I would have used String class, but didn't wan't to confuse the issue (cause he gave all C syntax).  I simply tried to demonstrate (and hopefully this point is still there) that there are better ways to initialize, protect, and manipulate variables in C++ than structs and defines.  Thank you for pointing out some of the errors.
0
 
jtwine100697Commented:
The comment was more for the OP than you, but thanks for explaining.

-=- James.
0
 
jcgdCommented:
replace the line:
Person VarName = { 0,
with
Person VarName = {
0
 
vbk_bgmAuthor Commented:
That was a typo that I forgot to remove. Actually I meant Person VarName = {  instead of Person VarName = {0,
0
 
jcgdCommented:
without the last '\' the macro is working well in VC6...
0
 
vbk_bgmAuthor Commented:
Sorry once agian. The correct macro should read as follows:
#define BEGIN_INIT_PERSON(VarName) \
     Person VarName = {  
     
#define AGE(val)    \
          val,          


#define NAME(nam)    \
          nam,

#define END_INIT_PERSON()   };

The typo does not affect the compilation since there isn't anything on the next line.
BTW, Will anyone help me in the solution?
0
 
jcgdCommented:
Maybe this macro help you:

#define BEGIN_INIT_PERSON(VarName) \
    Person VarName =  
#define AGE(val)    \
          {val
#define NAME(nam)    \
               ,nam}
#define END_INIT_PERSON()   ;
0
 
vbk_bgmAuthor Commented:
Hi jcgd,
       I have tried that but it does not give any compilation error with the foll usuage:
BEGIN_INIT_PERSON(p1)
AGE(2)
END_INIT_PERSON()
user has missed the name macro, and it does not emit an error.
0
 
jcgdCommented:
I receive an error (in VC6++), because if if don't write AGE(x) and NAME(x) no brackets are written and initialization struct without brackets it's an error compile.
0
 
vbk_bgmAuthor Commented:
Yes it works.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.