Solved

union initalization

Posted on 1999-01-22
11
1,267 Views
Last Modified: 2008-02-01
Hello,

my problem is the following.
I want to allow the static initalization of a record array.
The record has one field supposed to be a union of different types.

It's structure is the following:

typedef struct {
   char* name;
   int type;
   union {
       int x;
       char* str;
       double flt;
   } val;
} fieldRec;

fieldRec recordDef[] = {
    { "field1", intType, 3 },
    { "field2", strType, "a string" },
    { "filed3", fltType, 0.123456789 }
};

This is wraped inside of a macro but this is what is effectively written.

The problem is that static initalization of union can only be done with using the first type of the union.
Casting to the first type is not a good solution.
It may work with pointers on most computers but will fail with double since it does a conversion and not just a cast.
(I use -int64 under visual C++) The value given in the example will be converted to zero.

My objective is to provide an easy and intuitive way to define and initialize this field definition vector.
I want this initialization to be static because there are plenty of them.

Thus I would like to avoid constructors if possible and if it could be compatible with C, it would be the best solution.

Usage of a macro to wrap the field declaration is ok. Avoiding the need for the user to cast the given value would be allright.

Any suggestion ?

The points will be increased if the answers satisfies all requirements.
0
Comment
Question by:meessen
  • 6
  • 3
  • 2
11 Comments
 
LVL 13

Expert Comment

by:Mirkwood
ID: 1184835
Sorry. Read the KB article below



INFO: Initializing Unions Initializes First Member of the Union
Last reviewed: September 2, 1997
Article ID: Q47693  
The information in this article applies to:
Microsoft C for MS-DOS, versions 6.0, 6.0a, 6.0ax
Microsoft C/C++ for MS-DOS, version 7.0
Microsoft Visual C++ for Windows, versions 1.0, 1.5, 1.51, 1.52
Microsoft Visual C++ 32-bit Edition, versions 1.0, 2.0, 2.1, 4.0, 5.0


SUMMARY
When initializing a union, the initialization value is applied to the first member of the union even if the type of the value matches a subsequent member. As stated in the ANSI Standard, Section 3.5.7:


   A brace-enclosed initializer for a union object initializes the
   member that appears first in the declaration list of the union
   type.

Because you cannot initialize the value of any member of a union other than the first one, you must assign their values in a separate statement. Initializing a union with a value intended for a subsequent member causes that value to be converted to the type of the first member.


MORE INFORMATION
The following example demonstrates the issue:



Sample Code

   /* Compile options needed: none
   */

   #include <stdio.h>
   union { int   a;
           float b;
         } test = {3.6};    /* This is intended to initialize 'b'      */
                            /* however, the value will be converted    */
                            /* (first to a long and then to an int)    */
                            /* in order to initialize 'a'.             */

   void main (void)
   {
      float dummy = 0.0;            /* This causes the floating point  */
                                    /* math package to be initialized.  */
                                    /* Not necessary with VC++ for     */
                                    /* Windows NT.                      */

      printf ("test.a = %d,  test.b = %f\n", test.a, test.b);
   }

The output from the example, though not what is intended, is as follows:

   test.a = 3, test.b = 0.00000

To associate a value with "b", you can reverse the order of the members, as in the following:

   union {
           float b;
           int a;
         } test = {3.6};

Or, you can retain the order of the elements and assign the value in a separate statement, as in the following:

   test.b = 3.6;

Either of these methods creates the following output:

   test.a = 26214, test.b = 3.600000

Under Windows NT, the output would be as follows:

   test.a = 1080452710, test.b = 3.600000

REFERENCES
For examples and explanation of possible compiler errors and warnings generated when attempting to initialize a non-primary union element, please see the following article in the Microsoft Knowledge Base:


   ARTICLE-ID: Q39910
   TITLE     : PRB: Initializing Non-Primary Union Element Produces Errors
Keywords          : CLngIss kbcode kbfasttip
Version           : MS-DOS:6.0,6.00a,6.00ax,7.0; WINDOWS:1.0,1.5,1.51,1.52;  WINDOWS NT:1.0,2.0,2.1,4.0,5.0
Platform          : MS-DOS NT WINDOWS
Issue type        : kbinfo
 


--------------------------------------------------------------------------------

================================================================================


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: September 2, 1997
) 1998 Microsoft Corporation. All rights reserved. Terms of Use.
 


0
 
LVL 22

Expert Comment

by:nietod
ID: 1184836
It can't be done that way.  But there is a way to reach your goal.

Use a class instead of a structor (actually not necessary, you can do it with struct) and provide constructors for each of the cases you want to support.  those constructors can perfom the initialization.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1184837
It would be used like

FieldRec recordDef[] = {
      FieldRec( "field1", intType, 3 ),
      FieldRec( "field2", strType, "a string" ),
      FieldRec( "filed3", fltType, 0.123456789) };

Not quite as convenient, but pretty good.  Let me know if you have any questions.
0
Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
LVL 1

Author Comment

by:meessen
ID: 1184838
Mirkwood, I rejected your answer not because what you say is wrong, but becasue it does not answer my question. I knew already what is contained in your answer for the static initialization constrain on unions.

In your document it said the following:

"...
Or, you can retain the order of the elements and assign the value in a separate statement, as in the  following:
   test.b = 3.6;
."

Just to be sure, is this an instruction, not a static initialization ?
I don't want instructions because there might be hundreds (up to one tousand) of such vectors defined in a program. Initializing all theses by instructions would make program initialization havy.

I thought of using static initializer much more efficient since they can all be copied into memory in one block.


About uding classes
-----------------------------
This look like a  good solution.

The solution will not be C compatible and how is the class initialized ? Will it be a static initialization ?

Suppose I define the constructor as

field( char* name, int type, char* str):val.str(str){}
field( char* name, int type, int x):val.int(x){}

Whera val is a union inside the structure or class with one field named str of type char* and another field named int  of type int.

Will this be equivalent to a static initialization ?
 





0
 
LVL 13

Expert Comment

by:Mirkwood
ID: 1184839
Nope, what you describe is code and code is executed while initialization is just there. Code cannot be outside a code block so that won't work.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1184840
>> The solution will not be C compatible and how is the class initialized ?
>> Will it be a static initialization
Nope.  Constructors are performed only at run time.

0
 
LVL 1

Author Comment

by:meessen
ID: 1184841
Related question

If I define a global

int ar[] = {1,2,3,4,5,6,7,8,9 };

Will there be instructions executed to initialize each of these globals ?
In VC++ the debugger moves to each global initalization as an instruction step.
Does it mean, each initialization is equivalent to an instruction or will all these variables be initialized with a single bloc copy into memory ? I know that this is how it worked with older Mac compilers and my work like this with unix compilers. This is the data bloc in the binary file.

If this is equivalent to an instruction, it may be wiser to avoid static initialization because there are many of them and most of the time only a few number of them are effectively used. In this case a  Just In Time initialization would be more appropriate.

The question would then move to :

What macro to define in ordre to wrap definition of a function initalizing the array which might also be dynamic.

The function might look like this

fieldRec* myArray(){
         static fieldRef* val = NULL;
         if( val || !(a = malloc( X )) ) return a;
        a->name = "myName";
        a[0]->type = 1;
        a[0]->val.flt = 0.452;
      ....
}

But I would like that the user only writes/see this

MESSAGEDEF(NAME){
          FIELDDEF(TYPE,VALUE);
          FIELDDEF(TYPE,VALUE);
          FIELDDEF(TYPE,VALUE);
          FIELDDEF(TYPE,VALUE);
} ENDMESSAGEDEF;

Where FIELDEF and the value type  may vary from message to message.

0
 
LVL 22

Expert Comment

by:nietod
ID: 1184842
>>If I define a global

>>     int ar[] = {1,2,3,4,5,6,7,8,9 };

>>     Will there be instructions executed to initialize each of these globals ?
That is implimentation defined (meaning that it is up to the compiler manufacturer and we can not say).  However, I can't image that any will not have the information pre-initialized in a release version.  I can't promise you that, but I would count on it.  In the release versions, VC does currently have static information pre-initialized at the moment.  I'm surprised it doesn't in the debug versions.  Now this only applies to POD (Plain old data), data that does not have a constructor.  I there is a constructor, it will be run at load time.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1184843
>> If this is equivalent to an instruction, it may be wiser to
>> avoid static initialization because there are
>> many of them and most of the time only a few number
>> of them are effectively used. In this case
>> a  Just In Time initialization would be more appropriate.
you can safely use static intitialization for POD.  Reserve just in time initialization for classes with constructors.  But even then, only of there is a really long initialization process.  Have you tried any timing studies to see if this is a problem, you may be worried about something that is going to be increadibly fast.  A pentium should be able to initialize a memory location with an immediate in a single clock cycle. There is not likely to be any wait for EA calculation in these cases, but there may be a wait for the bus (not ussualy) and there may be a wait of the data is aligned wrong (again that should not happen), so lets say the worst case is 3 clock cycles.  (1 is probably far more likely).  Then on a 300Mhz computer you should be able to initailize 100 million integers in a second.  You have to share CPU time with other programs and the OS, so maybe lets cut that down to 25 million.  Isn't that sufficient for your needs?  And that's a worst case.  I expect the average to be far better.
0
 
LVL 1

Author Comment

by:meessen
ID: 1184844
I didn't tried any timing studies. Your numbre are reassuring me.

Yesterday I implemented a version using dynamic initialization.
The definition macro now defines a function.
It is written like this

defPtr myMessageDef(){
    static defPtr it = NULL;
    if( it ) return it;
    it = malloc(.....);
    ...

It works well. A definition now looks like

MR_DEF( myMessage  )
    MR_CST( field1, mr_INT, 12345 );
    MR_CST( field2, mr_DBL, 0.12345 );
    MR_CST( field3, mr_STR, "This is a string");
MR_DEF_END;

I now need this MR_DEF_END because I need to close the braket and return the initialized dynamic bloc.

The good news is that I only initialize when I really need it, that I can now also add a hash value of the definition which might be usefull to distinguish between historic variation of the same message definition between compiled programs of different generation. This was a problem left open by using static initializers.

So now I'm happy with the solution I came up with. I would like to be honnest and share the points between the people that proposed their help and spent some precious time for me.

There is a same question on the C list. Nietod you can answer here, you get the points and if mirkwood answer on the c list he gets the point.
I'm not satisfied at all with the answers I got on the C list.
0
 
LVL 22

Accepted Solution

by:
nietod earned 100 total points
ID: 1184845
I still don't think this is necessary, but In C++ or (I think) C you could use the fact that local statics are initialized only when the function is first called.  (never before it is called and not on each call) thus you could do

defPtr myMessageDef(){
         static defPtr it = malloc(...)
         return it;
};

Or you might be able to avoid the malloc completely and just return a staticlly declared -- whatever.  I.e. make the data staticlly declared, rather than a pointer to it.

>> 'm not satisfied at all with the answers I got on the C list.
I'm not surprised.  

                                                           todd niec--official C++ snob.
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Unlike C#, C++ doesn't have native support for sealing classes (so they cannot be sub-classed). At the cost of a virtual base class pointer it is possible to implement a pseudo sealing mechanism The trick is to virtually inherit from a base class…
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

860 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question