C, Portable way to deprecate struct members - c

With GCC & Clang you can deprecate struct members, (as shown below).
However I didn't see a way to do this for other compilers (MSVC for example).
While this isn't apart of the C spec and most likely relies on pragma's or custom extensions for each compiler, it would be useful to be able to support this across a wider range of compilers.
/* mytest.h */
#ifdef __GNUC__
# define ATTR_DEPRECATED __attribute__((deprecated))
#else
# define ATTR_DEPRECATED /* unsupported compiler */
#endif
struct Test {
int bar;
int foo ATTR_DEPRECATED;
};
Once a member is deprecated, the compiler should warn of its use if its accessed directly, eg:
#include "mytest.h"
static func(void)
{
Test t;
t.bar = 1;
t.foo = 0; /* <-- WARN ABOUT THIS AT COMPILE TIME */
}

Besides compilers which support GCC's __attribute__((deprecated)) (Clang for example) there aren't conventions for MSVC to deprecate struct members (With MSVC you can deprecate the identifier but that applies globally, not just to that struct).
Also no other conventions for other C compilers have been posted. So it looks like this is GCC specific.

Put simply, there isn't a one-way fits all thing here. Some compilers simply don't support such ideas. I will suggest something along the lines of this however, duplicated here (with some modification):
#ifdef __GNUC__
#define ATTR_DEPRECATED __attribute__((deprecated))
#elif defined _MSC_VER
//List of deprecated values
#pragma deprecated(values)
#endif
Unfortunately, this requires a #pragma line for every deprecated member but hey.
Also, as ideasman points out in the comments, the deprecated pragma is global, so if you have an instance with the same name as your struct member anywhere, it will be marked.

Perhaps something more along the lines of this:
#ifdef __GNUC__
#define DEPRECATED(x) x __attribute__((deprecated))
#elif defined _MSC_VER
#define DEPRECATED(x) __pragma deprecated(x)
#else
#define DEPRECATED(x) x
#endif
edit: this is based on Quirliom's answer
2nd edit: fixed #pragma to be __pragma as suggested by ideasman42

If all of deprecated structure members have names which are unique to the structure, it may be possible to replace the structure with one that contains an array of a structure containing the real values but slightly different names, and then use #define to create macros that define all of the structure's elements.
For example, if the old structure was:
typedef struct {
int FOO_first;
int FOO_second;
long FOO_third;
} FOO;
one could replace it with:
#define WARNING_ZERO (2u < -1) // An expression which will yield a constant zero but print a warning
#define FOO_second (FOOO_second[WARNING_ZERO])
typedef struct {
int FOO_first;
int FOOO_second[1];
long FOO_third;
}
Unfortunately, I don't know any way to make that technique work in C if the names of any deprecated items are used as identifiers for any other purpose.

Related

GCC reporting unrelated errors for include

I've added a new file to a project:
#ifndef PLAYER_H
#define PLAYER_H
#include "enet/enet.h" //the problem
typedef struct Player
{
ENetPeer * peer; //requires problematic include
//void * peer; //works, since no include required
} Player;
const struct Player playerEmpty;
#endif //PLAYER_H
If the include is present, I get reams of error: expected ';', ',' or ')' before numeric constant in unrelated files. If I remove the include and use void * peer instead, all is well. The enet library is included in a source file elsewhere, and works fine. I am using enet 1.3.13 (latest) and its header guards appear to be in place. This is under gcc 4.9.2.
For the record the errors are occurring in Point.h:
#ifndef POINT_H
#define POINT_H
#include <stdint.h>
#define X 0
#define Y 1
#define Z 2
typedef int16_t int16_Point2[2];
typedef int32_t int32_Point2[2];
typedef uint16_t uint16_Point2[2];
typedef uint32_t uint32_Point2[2];
typedef int16_t int16_Point3[3];
typedef int32_t int32_Point3[3];
typedef uint16_t uint16_Point3[3];
typedef uint32_t uint32_Point3[3];
#endif //POINT_H
I'm sure it's something simple - any idea what I am doing wrong?
Using single-letter macro names is a good idea in general. They might very easily replace the letters in unexpected locations (note: Macros are actually textual replacement before the actual compilation phases).
You wrote the error is occuring in Point.h. I do not think they actually occur, but are only reported here. C is notoriously bad to detect grammar errors where they actually are. Check the file which includes Point.h
Note: const struct Player playerEmpty; in a header is also likely unwanted, as that will create an object with external linkage in every compilation unit. This is different from C++: in C, there are actually no constants, but only constant variables: const is just a promise by the programmer the variable will never be changed once initialized. Even worse: you do not assign it a value, thus making it effectively 0 - global variables are initialized to all bits 0. I'm quite sure this in not intended.
Update:
If that is for points, how about:
typedef union __attribute__ ((__packed__)) {
struct {
int16_t x,y,z;
}; // anonymous union field (C99)
int16_t vec[3];
} int16_Point3;
...
// usage:
int16_Point3 point = (int16_Point3){ .x = 5, .y = 3 }; // compound literal
point.z = point.x + point.vec[1]; // other word for point.y
to get rid of the #defines and getting proper syntax.
Note __attribute__ ((__packed__)) is gcc-specific to avoid padding bytes between struct fields. That is non-standard, but other compilers often have similar features (e.g. pragma). It is required to have identical layout for the struct and the array.
That might be more readable than the indexing. Note that anonymous struct and union fields are standard.
The problem was single-character #defines. NEVER do this.
I'd been using X, Y and Z for several months, but never had problems till my inclusion of Player.h, today, which must have finally - in a roundabout way - triggered some problems in the preprocessor / compiler. Removing these returned compilation to (a semblance of) normality.
Thanks to those who helped in the comments.

How to conditionally compile C code based on size of data?

I define below data type:
typedef int MyInt;
Then I hope to define a new data type based on size of MyInt, something like below code, but I can't find a solution for it.
#if sizeof(MyInt) == 2
typedef long MyLong;
#else
typedef short MyLong;
#endif
Could anybody help?
It's not the preprocessor's job to evaluate sizeof, that's done by the compiler which is a later stage in the process. Evaluating sizeof needs deep C knowledge that the preprocessor simply doesn't have.
You could (in theory) consider the preprocessor as a separate step, that does text only transforms, in effect converting "foo.c" to "foo-preprocessed.c". The latter file won't have any #include or #ifs left, they're all evaluated and replaced by the preprocessor. The actual compiler never sees them.
You should consider using <stdint.h> and the known-precision types (uint16_t and friends).
You could use the values of macros such as UINT_MAX to determine the size of the underlying type.
You can use UINT_MAX - it can give you a clue about the size of integer.
#if (UINT_MAX <= 65536)
typedef long MyLong;
#else
typedef short MyLong;
#endif
New-enough GCC (I think 4.3) has predefined identifiers like __SIZEOF_LONG__, but it is simpler to just use the constants in limits.h as suggested by others.
Although I and the C preprocessor are sworn enemies, we sometimes need each other ;). So I would propose:
#if SIZEOF_MYINT == 2
# define MYLONG_T long
#elif SIZEOF_MYINT == 4
# define MYLONG_T short
#endif
typedef MYLONG_T MyLong;
Somewhere else (in a global config header file) you have your architecture dependent
#define SIZEOF_MYINT 2

How to close specified warning in C source code?

For example, how can I get rid of "warning: unnamed struct/union that defines no instances" in the source file, not through compiler command-line options.
I want to define a C macro CONST_BUG_ON, which I use to check some const values at compile time.
#define CONST_BUG_ON(e) struct {int a:!(e);}
It gives the warning warning: unnamed struct/union that defines no instances, but in this case it is not a real problem.
Thanks Tom Tanner
#define CONST_BUG_ON_3(e, l) struct buggy##l {int a:!(e);}
#define CONST_BUG_ON_2(e, l) CONST_BUG_ON_3(e, l)
#define CONST_BUG_ON(e) CONST_BUG_ON_2(e, __LINE__)
That's good, but still have some problems: If file a's line 6 contain CONST_BUG_ON(e), and file a was inclued by file b, and line 6 of file b aslo contains CONST_BUG_ON(e), then gcc complains redefine error. Use__COUNTER__ instade of __LINE__ may perfect, but my old compiler does not support __COUNTER__.
Thanks Basile Starynkevitch
#define CONST_BUG_ON(e) do { \
int tab[__builtin_constant_p(e)?1:-1] = {0}; \
if (tab[0]) abort(); } while (0)
This is a C statement, can only be place in a function, I really want to use it outside the function.
One way to resolve the compiler’s complaint is that you have an unnamed struct that defines no instances is to give it a name:
#define CONST_BUG_ON(e) struct ForDebuggingOnly {int a:!(e);}
An alternate way to get the expression testing you want is to declare (but not define) an array that has an illegal size if e is true:
#define CONST_BUG_ON(e) extern int ForDebuggingOnly[(e) ? -1 : 1]
You could use macro magic to give yourself a unique ID by passing in the line number
#define CONST_BUG_ON_3(e, l) struct buggy##l {int a:!(e);}
#define CONST_BUG_ON_2(e, l) CONST_BUG_ON_3(e, l)
#define CONST_BUG_ON(e) CONST_BUG_ON_2(e, __LINE__)
That's fairly icky but it does give a unique name each time it is used (the 2nd level of indirection may be spurious, but this is what I have in some code that has stood the test of time).
It looks like what you try is called a compile time assertion or compile time assert macro. There are various ways to do this, usually involving arrays with negative dimension when the assertion fails. Many projects call this macro CT_ASSERT() and there are a bunch of Stackoverflow questions relating to them.
Assuming a recent GCC compiler, you could use __builtin_constant_p to test for compile-time constants, perhaps with
#define CONST_BUG_ON(e) do { \
int tab[__builtin_constant_p(e)?1:-1] = {0}; \
if (tab[0]) abort(); } while (0)
For your question about ignoring some warning, perhaps the GCC diagnostic pragmas could help.
If you want your CONST_BUG_ON to work only in declaration contexts, you might try
#define CONST_BUG_ON(e) CONST_BUG_AT(e,__LINE__)
#define CONST_BUG_AT(e,l) \
extern int tab_##l[__builtin_constant_p(e)?0:-1];
At last you could even customize your GCC compiler (with your specific pragma) perhaps using MELT (a high-level domain specific language to extend GCC), but that will take you days of work.

How do I do different things per macro value?

#define TYPE char *
if TYPE is char *
do A
if TYPE is int
do B
Is there an example how to do such things?
C preprocessor MACROS manipulate text, so are essentially typeless, so NO you can't do that.
You could associate another symbol with it:
#define TYPE char *
#define TYPE_IS_CHAR_STAR
#ifdef TYPE_IS_CHAR_STAR
...
#endif
You just need to keep them consistent manually.
Note that that's a dangerous macro; you should use a typedef instead. With the macro:
TYPE x, y;
x is a pointer, but y isn't.
You can get a similar effect by defining another macro along with the type, and using #ifdef etc. with that other macro. For example:
#define TYPE char *
#define TYPE_IS_PCHAR 1
...then later...
#ifdef TYPE_IS_PCHAR
do A
#endif
#ifdef TYPE_IS_INT
do B
#endif
It's not quite the same thing, but it still gets you there.
Not easily. You could do something like:
#define TYPE_IS_CHARPTR
//#define TYPE_IS_INT
#ifdef TYPE_IS_CHARPTR
do A
#endif
#ifdef TYPE_IS_INT
do B
#endif
But you really should be trying to minimise your use of the preprocessor for tricky things (anything other than simple variables).
With enumerated constants and inline functions, there's little need for such uses nowadays.
It would work if you just used basic types (since they're just strings - see Mitch's answer). But as soon as you try to use pointers, it won't work any more - the asterisk throws the preprocessor for a loop:
[holt#Michaela ~]$ gcc test.c
test.c:3:10: error: operator '*' has no right operand
But if you want do do different things based on different types, I'm going to have to recommend switching to C++ and using templates and template specialization. Reluctantly, since template syntax is incredibly ugly, but you should be able to do whatever you want.
Hope that helps!

compiling on windows and linux

I am new to c, and I have some been given some source code that someone else has written that was compiled on windows.
After trying to make in compile on linux I have errors because linux doesn't support DWORD, WORD, AND UINT32. I have 6 files for example. A.h, A.c, B.h, B.c, C.h, C.c. These keyword are in all the files.
So I am thinking of 2 possible solutions. Which is better #define or typedef.
1)
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned int UNINT32;
2)
#define DWORD unsigned long
#define WORD unsigned short
#define UINT32 unsigned int
For the second part I am wondering where should I put these declarations. Should they go in the header files, or should they go in the source files?
For example should I do something like this in the header files, or in the source files?
#ifdef WIN32
/* windows stuff */
#else
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned int UNINT32;
#endif
Many thanks for the above suggestions,
You have found the solution yourself:
#ifdef WIN32
/* windows stuff */
#else
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned int UNINT32;
#endif
Put this in a separate header file (typedefs.h) and include it from everywhere. Typedef are always preferred over pre-processor macros.
My recommendation: Do not use DWORD, WORD or other Win32 types. I usually prefer to use C99 standard types: uint_t, int_t or uint16_t, uint32_t
Typedefs are definitely nicer. #defines are preprocessor macro's and can have unintended consequences, because basically the C preprocessor performs a global search-and-replace for defines. Typedefs are instructions to the compiler, and much better suited for what you want to do.
typedef would be better in this instance because #define is just a generic mechanism but typedef is for defining types which is what you are doing.
I would say put your code:
#ifdef WIN32
/* windows stuff */
#else
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned int UNINT32;
#endif
into a new header file (with #define guards/#pragma once), then include that from the header or source files as necessary.
Using a typedef turns the result into an actual type that gets put into the syntax tree. (In other words, the compilers knows about it and recognizes it as a part of the language.)
#define, in contrast, is just a text-substitution. So the compiler never gets to know about it, it instead just sees whatever it is that gets substituted. This can make finding compile errors harder.
For your case, I would probably recommend typedef. #define has it's place, but I can't see any reason why you wouldn't want to use typedef here.
Be aware that other libraries may have defined these types, so you may have collisions. If you really want to be cross-platform, you might think about defining types with your app's namespace somehow. Like
myapp_dword
myapp_word
in order to minimize collisions with other libraries.
Finally, I would actually recommend against the entire approach you are taking. If at all possible, it is best to use only the typenames defined in the language and in the C standard library (like size_t, etc.) Your code will be more portable, and you will have less headaches.

Resources