Does using const global vars instead of enum ensure ABI compatibility? - c

In a C library project of mine I have an enum that lists all possible types a piece of data handled by the library can be:
// lib.h
enum types {
VOID,
INT,
FLOAT,
CONST_INT,
CONST_FLOAT
}
The code will be compiled into a shared library. In future versions of the library, I will be required to insert new entries into the enum and reorder existing ones. AFAIK this breaks ABI compatibility since the enum is not transformed into a set of symbols that ends up in the library, but rather causes whatever integer the compiler assigns to each entry to be hardcoded. Is that the case?
If so, would it be preferable to instead use constant global variables such that they make an appearance in the symbol table and I thus can change both order and the value assigned to each?
// lib.c
const int VOID = 1;
const int INT = 2;
const int FLOAT = 3;
const int CONST_INT = 4;
const int CONST_FLOAT = 5;
// lib.h
extern const int VOID;
extern const int INT;
extern const int FLOAT;
extern const int CONST_INT;
extern const int CONST_FLOAT;

You're right. If you plan to expand the enumered variables it could make incompatible the future versions of your libraries.
If you take a look to some of the most relevant sw around (i.e. MS or linux headers) you can see that the solutions adopted are mainly two:
Use defines
Still use enum, but assign a value per each entry
The latter makes the use of enum quite equal to defines, but retains the properties of enums.
In your case it could be:
// lib.h
enum types {
VOID = 0,
INT = 1,
FLOAT = 10,
CONST_INT = 12,
CONST_FLOAT = 13
}
Then when in future you will add other codes:
// lib.h
enum types {
VOID = 0,
INT = 1,
CUSTOM1 = 3,
FLOAT = 10,
CUSTOM2 = 11,
CONST_INT = 12,
CONST_FLOAT = 13,
CUSTOM4 = 20
}
The use of constant globals could have some problems, even to be optimized out and be replaced by constants.

best way is to place the enum in the header file for that library.
Changing the enum will still require re-compiling/linking etc of the library (no getting around that),
To assure each label in the enum has the specific value, the enum can be written similar to:
enum
{
label1 = 0,
label2 = 1,
label3 = 20,
label4 = 5
};
the results in anonymous enum, where each of the labels are visible and have the appropriate value.

Related

How to assign a predefined value to a struct member on type definition?

This is a long shot, but maybe there will be some ideas. On a system I programming, I have defined structures to program processor registers. The registers are comprised of several fields of a few bits each, with potentially "reserved" bits in between. When writing to a register, the reserved bits must be written as zeros.
For example:
typedef struct {
uint32_t power : 3;
uint32_t reserved : 24;
uint32_t speed : 5;
} ctrl_t;
void set_ctrl()
{
ctrl_t r = {
.power = 1;
.speed = 22;
.reserved = 0;
}
uint32_t *addr = 0x12345678;
*addr = *((uint32_t *) &r);
return;
}
I want to be able to set the reserved field to a default value (0 in this example), and to spare the need for an explicit assignment (which happens a lot in our system).
Note that if the instantiated object is static, then by default an uninitialized field will be 0. However, in the above example there is no guarantee, and also I need to set any arbitrary value.
Structure type definitions in C cannot express values for structure members. There is no mechanism for it. Structure instance definitions can do.
I want to be able to set the reserved field to a default value (0 in
this example), and to spare the need for an explicit assignment (which
happens a lot in our system).
Note that if the instantiated object is static, then by default an
uninitialized field will be 0. However, in the above example there is
no guarantee, and also I need to set any arbitrary value.
That the default value you want is 0 is fortuitous. You seem to have a misunderstanding, though: you cannot partially initialize a C object. If you provide an initializer in your declaration of a structure object, then any members not explicitly initialized get the same value that they would do if the object had static storage duration and no initializer.
Thus, you can do this:
void set_ctrl() {
ctrl_t r = {
.power = 1,
.speed = 22,
// not needed:
// .reserved = 0
};
// ...
If you want an easy way to initialize the whole structure with a set of default values, some non-zero, then you could consider writing a macro for the initializer:
#define CTRL_INITIALIZER { .power = 1, .speed = 22 }
// ...
void set_other_ctrl() {
ctrl_t r = CTRL_INITIALIZER;
// ...
Similarly, you can define a macro for partial content of an initializer:
#define CTRL_DEFAULTS .power = 1 /* no .speed = 22 */
// ...
void set_other_ctrl() {
ctrl_t r = { CTRL_DEFAULTS, .speed = 22 };
// ...
In this case you can even override the defaults:
ctrl_t r = { CTRL_DEFAULTS, .power = 2, .speed = 22 };
... but it is important to remember to use only designated member initializers, as above, not undesignated values.
It can't be done.
Values don't have "constructors" in the C++ sense in C. There's no way to guarantee that arbitrary code is run whenever a value of a certain type is created, so this can't be done. In fact "creation" of a value is quite a lose concept in C.
Consider this:
char buf[sizeof (ctrl_t)];
ctrl_t * const my_ctrl = (ctrl_t *) buf;
In this code, the pointer assignment would have to also include code to set bits of buf to various defaults, in order for it to work like you want.
In C, "what you see is what you get" often holds and the generated code is typically quite predictable, or better due to optimizations. But that kind of "magic" side-effect is really not how C tends to work.
It is probably better to not expose the "raw" register, but instead abstract out the existance of reserved bits:
void set_ctrl(uint8_t power, uint8_t speed)
{
const uint32_t reg = ((uint32_t) power << 29) | speed;
*(uint32_t *) 0x12345678 = reg;
}
This explicitly computes reg in a way that sets the unused bits to 0. You might of course add asserts to make sure the 3- and 5-bit range limits are not exceeded.

How can I add 2 ints in a const definition in 1990?

Welcome to the 1990s,
I am using an old Mac os 7.01 API, and I need to define a "Rect" struct with an array of four constants. Sadly, I always get the "requires constant" error on that "Rect" definition. We are talking about a 24 year old compiler, though.
Rect shapeRect = {100, 100, 200, 200}; // Works
const int shapeSize = 10;
int shapeX = 0; // Cannot be const
int shapeY = 0; // Cannot be const
Rect shapeRect = {shapeX - shapeSize, shapeY - shapeSize, shapeX + shapeSize, shapeY + shapeSize }; // Error: "requires constant"
I've tried defining multiple const with all the 4 values calculated, but I still get the same error on the same line.
const shapeRectT = shapeX - shapeSize;
...
Rect shapeRect - {shapeRectT, ...};
My guess is that the shapeRectT const is not a constant? I am a beginner in C, but I believe this problem is probably easy to repair, but this is an old compiler, and things may be different, and I don't know if newer C standards changed things about this stuff.
EDIT: I found the documentation for this API (QuickDraw): https://developer.apple.com/legacy/library/documentation/mac/pdf/ImagingWithQuickDraw.pdf
The problem is related to limitations of C89 that we all now have forgotten.
At that time you could not have non-litterals in the initializer of a global struct variable. Non-litteral expressions were only allowed for local struct variables.
This restriction was due to the fact that the code execution really started in those time with main() (in fact a stub initializing stdin,stdout and the environment and then calling main). There was no code generated for global initialisation. Only values that where loaded as part of the image from the executable.
I just could confirm this behaviour with my old microsoft compiler. It was MSDOS, but from February 1990, so pretty close to the one you use:
struct R {int a, b; }; /* simple structure for the demo */
struct R a = { 1,2 }; /* global variable with litteral init: ok ! */
const int v1 =10;
const int v2 =20;
struct R z = { v1,v2 }; /* Error message, "illegal initialization" */
int main()
{
struct R w = { v1,v2}; /* here it is accepted, because corresponding
initialisation code could be generated/executed
as part of the function */
return 0;
}
Yes, there is a difference in aggregate (and union) initialization between C89 and C99. A quote from the C99 rationale (PDF page 95):
The C89 Committee considered proposals for permitting automatic aggregate initializers to consist of a brace-enclosed series of arbitrary execution-time expressions, instead of just those usable for a translation-time static initializer. Rather than determine a set of rules which would avoid pathological cases and yet not seem too arbitrary, the C89 Committee elected to permit only static initializers. This was reconsidered and execution-time expressions are valid in C99.
Some C89 compilers implement this as extension. Yours doesn't. You can fix the error like this:
Rect shapeRect;
shapeRect.top = shapeX - shapeSize;
shapeRect.left = shapeY - shapeSize;
shapeRect.bottom = shapeX + shapeSize;
shapeRect.right = shapeY + shapeSize;
Do it manually. (Not recommended.)
Make some constants you use for initializing (not const-qualified variables).
enum {shapeX_Init = 0, shapeY_Init = 0, shapeSize_Init = 10};
const int shapeSize = shapeSize_Init;
int shapeX = shapeX_Init; // Cannot be const
int shapeY = shapeY_Init; // Cannot be const
Rect shapeRect = {shapeX_Init - shapeSize_Init, shapeY_Init - shapeSize_Init,
shapeX_Init + shapeSize_Init, shapeY_Init + shapeSize_Init };

Difference between VS compiler and g++ in terms of the initialization of the variables in a struct

I want to declare a char array in a struct and initialize it when it is declared. I compiled it on g++ and VS2010 compilers. g++ could compile the following code but VS2010 couldn't. VS2010 has error.
From C++ primer, const static type data could be initialized when it is declared in a struct/class. But when I use it for the OPName array, both compilers reports errors. I can only use const on g++ to achieve that. Why does it happen?
What is the valid way to initialize array variables, like my example (i.e., char array), when they are declared in a struct/class?
Here is the code (.c file):
typedef struct OPMap {
const char OPName[27][6] = {"SW", "LW", // 2
"J", "BEQ", "BNE", "BGEZ", "BGTZ", "BLEZ", "BLTZ", // 7
"ADDI", "ADDIU", // 2
"BREAK", // 1
"SLT", "SLTI", "SLTU", // 3
"SLL", "SRL", "SRA", // 3
"SUB", "SUBU", "ADD", "ADDU", // 4
"AND", "OR", "XOR", "NOR", // 4
"NOP"}; // 1
unsigned int op[8][8];
unsigned int op_TLB[4][8];
unsigned int op_R[8][8];
const unsigned int NOP = 0x00000000;
OPMap() {
//......
}
} Map;
Before C++11, having an initializer for a data member in the class definition is only legal if the member is static const and also a non-struct, non-array (e.g. int or double). VS2010 does not comply with C++11 so you get that error.
For the code to work prior to C++11 you should change to:
static const char OPName[27][6];
and then in one of your .cpp files (i.e. not a header file) have:
static const char OPMap::OPName[27][6] = {"SW", "LW", // 2
"J", "BEQ", "BNE", "BGEZ", "BGTZ", "BLEZ", "BLTZ", // 7
"ADDI", "ADDIU", // 2
"BREAK", // 1
"SLT", "SLTI", "SLTU", // 3
"SLL", "SRL", "SRA", // 3
"SUB", "SUBU", "ADD", "ADDU", // 4
"AND", "OR", "XOR", "NOR", // 4
"NOP"}; // 1
In C++11 you can have this table in the class definition by adding in static constexpr .
There are multiple points here, the first one is that the rules are very different between C++98 and C++11. C++11 allows most in-class initialisation.
Supposing we are talking about C++98:
When you write static const int x = 3; in your class, the compiler "sometimes" accept it, that is when it can simply remove all reference to MyClass::x and substitute the value, just like #defines. However if you try to use x in anyway other than a const expression, you will get error. For example if you have a line stating:
const int *px = &MyClass::x;
It won't work because now the compiler needs to assign a place in memory for x, and not just substitute the value as before. Now you will have to define it in a cpp file, i.e
const int MyClass::x = 3;
And of course, this only work for integral types.
So in C++98, initialising class members are done in constructors, for instance variables, and by defining them in a cpp file, for static members. The in-class static const int x = 3; is just a shortcut that works "sometimes".
check out Defining static members in C++

Is it valid to use a C99-style designated initializer list to initialize the members of a bit field within a union in the following way?

When I wrote a question regarding PC-Lint, I had made an assumption that the following initialization is valid in C99. #JoachimPileborg mentioned that it may not be and I haven't been able to find any information that provides a good example one way or another. I know that it compiles and behaves as I expect, I would just like to know for certain that it is proper C99 code.
Is this a valid way to initialize the following union in C99?
typedef union
{
struct
{
unsigned int a : 4;
unsigned int b : 4;
unsigned int c : 4;
unsigned int d : 4;
} bits;
unsigned short value;
} My_Value;
int main (void)
{
My_value test[] =
{
{
.bits.a = 2,
.bits.b = 3,
.bits.c = 2,
.bits.d = 3,
},
{
.bits.a = 1,
.bits.b = 1,
.bits.c = 1,
.bits.d = 0,
},
};
/* Do something meaningful. */
return 0;
}
Looks sane... if your tame compiler doesn't complain with standards compliance cranked way up, I'd use it. Much more worrying is that you presumably are trying to overlay value and bits, and stuffing data into one alternative of an union an taking it out of the other is undefined. Endianness aside, the union will probably use up a full word, and very well could have the value at one end and the bits at the other (depending on available instructions and their convenience or timing). The standards declare this undefined precisely to give implementations such leeway.

C: Behaviour of the `const` keyword

I've been told that if I'm coding in ANSI C to declare in the order that the variables will be used, assert that pointers are not null and that indices are within bounds, and to initialize just before usage of the variable.
If I declare a const can I initialize it after a block of assertions and code?
In Java final initializations must occur at declaration, yet is it consistent through ANSI C implementations that I can initialize a const once, but not necessarily at the time of declaration?
The Java compiler has a small amount of flow logic to allow you to initalise final variables after their declaration. This is legal Java:
final int something;
if ( today == Friday )
something = 7;
else
something = 42;
Java will detect if any branches leave the final value undefined. It won't analyse the conditions, so this is not legal Java, even though it's logically similar:
final int something;
if ( today == Friday )
something = 7;
if ( today != Friday )
something = 42;
In ANSI C89, const variables ( other than extern ) must be initialised in the statement they are declared in.
const int something = ( today == Friday ) ? 7 : 42;
The extern modifier on a declaration tells the compiler that the variable is initialised in a different complation unit ( or elsewhere in this compilation unit ).
In ANSI C99, you can mix declarations and code, so you can declare and initialise a const variable after a block of assertions and code. Portability of 1999 ANSI C remains an issue.
A work around for C89 is to note that the rules for declarations preceding code work at block scope rather than function scope, so you can do this:
#include<stdio.h>
int main ( void )
{
printf ( "wibble\n" );
{
const int x = 10;
printf ( "x = %d\n", x );
}
return 0;
}
Be aware that even in C89, you can often move the definition closer to the point of first use by introducing a bare block just for the extra scope. Before:
int a, b, c;
a = 12;
// Do some stuff with a
b = 17;
// Do some stuff with a and b
c = 23;
// Do some stuff with a, b, and c
After:
int a = 12;
// Do some stuff with a
{
int b = 17
// Do some stuff with a and b
{
int c = 23;
// Do some stuff with a, b and c
}
}
With C99 of course, you can define variables other than at the beginning of a block:
int a = 12;
// Do some stuff with a
int b = 17
// Do some stuff with a and b
int c = 23;
// Do some stuff with a, b and c
const variables are read-only and must be initialised where they're defined.
This code produces error: assignment of read-only variable 'foo' (GCC 4):
const int foo;
foo = 4;
The same goes for const pointers (note here: const int * is not a const pointer, but a pointer to const):
int * const foo;
foo = 4;
Short of the block scope and C99 declaration methods other have shown, the answer is no; you cannot defer initialization of a const variable. Anyway, const is not very useful for local variables. The main times I use the const keyword in C are:
Pointers in function arguments (or local variable pointers based on arguments) where the function is honoring a contract not to modify the pointed-to data. The const keyword helps ensure that the function implementation respects the requirement not to modify (it requires special effort casting to get rid of const) and allows this requirement to propagate through multiple function calls.
For declaring compile-time constant tables (lookup tables, predefined permanent objects, etc.) which I want stored in a read-only section of the binary so they don't use extra physical resources at runtime.
I sometimes declare local variables const if I think it will assist the reader in understanding a function, but that's pretty rare.
You can't initialize the const after declaration within the function body, but you can just open one block after your assertions:
void func()
{
int y;
// Do assertions
assert(something);
{
int const x = 5;
// Function body
}
}
If you are talking of splitting a definition
const int x = 2;
into two parts:
const int x;
x = 2;
I'm afraid that's not possible in C.
If I were you, I would try to make sure I understand the intent of the coding rules that you describe. I doubt sane coding rules would prevent initializing variables (even non-const variables).
In response to various comments:
const int * p;
is not a declaration of a const variable. It is a declaration of a non-const pointer variable to a const int.
You can declare
extern const int x;
but you can still not initialize x after having executed code, assertion checks, etc.
If you'd like to cast away const on the LHS, use this:
const int n = 0;
*((int*)&n) = 23;

Resources