3

I hate defines. In the quest of eliminating as many of them as possible from an old code base, I need to rely on the enum-hack for defining structures containing arrays. Unfortunately, I'm not getting it to compile when the incriminated .h file gets included by C projects.

typedef struct _P_O {
    enum { MAXELEMENTS=10 };
    P_O_U elem;
    ULONG id[MAXELEMENTS];
    USHORT nid;
} P_O, *PP_O;

this produces error C2208:

'type' : no members defined using this type
An identifier resolving to a type name is in an aggregate declaration, but the compiler cannot declare a member.

OT: I hate using a compiler which is 10 y.o., having to mantain old c code and bad design too; not just defines :)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
Stefano Falasca
  • 8,837
  • 2
  • 18
  • 24
  • 6
    You can't have two elements called `id`...is that a transcription problem or the real problem? – Jonathan Leffler Aug 01 '13 at 15:13
  • transcription error, thanks!!! – Stefano Falasca Aug 01 '13 at 15:16
  • 3
    GCC warns: `warning: declaration does not declare anything` for the `enum` line. Place the `enum` outside the `struct` and you'll be fine. – Jonathan Leffler Aug 01 '13 at 15:16
  • @JonathanLeffler's answer compiles for me. – egrunin Aug 01 '13 at 15:22
  • @JonathanLeffler you should make your comment an answer – Stefano Falasca Aug 01 '13 at 15:23
  • My old clang does not warn about `declaration does not declare anything` like gcc does. – alk Aug 01 '13 at 15:31
  • @alk: I'm using `-Wall -Wextra`, but I'm getting the warning without adding any flags. For reference, I'm on Mac OS X 10.8.4 with `i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)`. With `clang`, I get `warning: declaration does not declare anything [-Wmissing-declarations]` using `Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn)` – Jonathan Leffler Aug 01 '13 at 15:40
  • @JonathanLeffler: I used `-Wall -Wextra -pedantic` nothing else (but `-o`). But as mentioned it's and "old" clang. It does not seem to be very popular to the Debian people, as on my x86_64 stable debian release (`Linux debian-stable 2.6.32-5-amd64 #1 SMP Fri May 10 08:43:19 UTC 2013 x86_64 GNU/Linux`) `clang --version` gives: `clang version 1.1 (Debian 2.7-3)` – alk Aug 01 '13 at 15:46

2 Answers2

6

Given the code in the question, GCC warns: warning: declaration does not declare anything for the enum line.

Place the enum outside the struct and you'll be fine.

typedef int P_O_U;
typedef unsigned long ULONG;
typedef unsigned short USHORT;

enum { MAXELEMENTS = 10 };

typedef struct _P_O
{
    P_O_U  elem;
    ULONG  id[MAXELEMENTS];
    USHORT nid;
} P_O, *PP_O;
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    the problem with this solution is that MAXELEMENTS is global, as it would have been if I used the original define. Is there any advantage of this solution over #define MAXELEMENTS 10 ? – Stefano Falasca Aug 01 '13 at 15:29
  • 1
    @StefanoFalasca why not just a `const static` variable ? – user2485710 Aug 01 '13 at 15:33
  • So you want to avoid the #define, and you don't need or want the value of `MAXELEMENTS` to be visible globaly? Why not just go to `ULONG id[19]` then? – AShelly Aug 01 '13 at 15:38
  • @user2485710 beause const static variables are a new addition of the language (the word "new" has a relative meaning, you see? :) ). It doesn't exist on my compiler – Stefano Falasca Aug 01 '13 at 15:39
  • @AShelly because it needs to be accessible by the extern world, which is different from it being global. Using the enum hack, as well as using a static const variable as suggested above (which is, of course, the only thing a normal person would do) would give you access to the number via _P_O::MAXELEMENTS – Stefano Falasca Aug 01 '13 at 15:40
  • @StefanoFalasca http://msdn.microsoft.com/en-us/library/vstudio/s1sb61xd%28v=vs.71%29.aspx/html and http://msdn.microsoft.com/en-us/library/vstudio/07x6b05d%28v=vs.71%29.aspx are you sure about this ? I found no doc about C but it looks like they are implemented, at least in C++. – user2485710 Aug 01 '13 at 15:42
  • In C, you don't have the scoping that C++ provides. If you have `struct tag1 { struct tag2 { int x; int y; } s; int x; int y; };`, the inner structure `struct tag2` is visible at global scope in C, but not in C++. – Jonathan Leffler Aug 01 '13 at 15:42
  • @user2485710: In C, you can't define `static const int MAXELEMENTS = 10;` and then use `MAXELEMENTS` in a structure definition — you can only do that in C++. – Jonathan Leffler Aug 01 '13 at 15:43
3
struct foo {
    enum { MAXELEMENTS=10 };
    long id[MAXELEMENTS];
};

enum hack like this is valid in C (But not a good style of code, see @Jonathan Leffler's comment). Because when id is defined, MAXELEMENTS qualifies with enumeration constant.

C99 6.2.1 Scopes of identifiers

Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. Each enumeration constant has scope that begins just after the appearance of its defining enumerator in an enumerator list. Any other identifier has scope that begins just after the completion of its declarator.

And an enumeration is a type of integer constant.

C99 6.6 Constant expressions

An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator.

Finally, an integer constant can be used in array declarators, see C99 6.7.5.2 Array declarators .That's not surprising.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
  • You should get a warning about `declaration does not declare anything`. It will probably compile, but not without warnings...and I'd rather have code that compiles without warnings. – Jonathan Leffler Aug 01 '13 at 15:24
  • @JonathanLeffler I agree with you that it is much better to compile with no warning. But, you see, it's not always possible (did you get a rough idea about the quality of the code I'm facing?) The problem here is that VS2003 doesn't compile it at all. – Stefano Falasca Aug 01 '13 at 15:27
  • @JonathanLeffler I agree with you, I will choose not to use like this, but I do think it is valid C, and it's what the OP asked. – Yu Hao Aug 01 '13 at 15:27
  • 2
    ISO/IEC 9899:2011, §6.7.2.1 **Structure and union specifiers** has syntax indicating that you can write `struct tag { int; double x; };` (the _struct-declarator-list_ is optional). However, writing such a line generates the warning. The `enum` line is similar — it has defined a type but not added a member to the structure, and that is permitted but (not unreasonably) generates the warning. So yes, it is 'legitimate' C; it is also legitimate for compilers to warn about it, and reasonable. Since compilers _do_ warn about it, and warning-free compilations are desirable, don't use the notation. – Jonathan Leffler Aug 01 '13 at 15:36
  • just, VS2003 doesn't warn about not adding a member; it gives you an error! :( – Stefano Falasca Aug 02 '13 at 07:21