20

I want to define a macro if a condition involving sizeof is true and do nothing (but still compile) if it is false. If the preprocessor supported sizeof, it would look like this:

#if (sizeof(void*) <= sizeof(unsigned int)) // what goes here?
#  define POINTER_FITS_INTO_UINT
#endif

There are some pages (e.g. http://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/) which explain how to make a compile-time assertion on sizeof (and fail to compile if it fails), but I don't see a way to extend this approach to what I want.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • ... And what exactly do you want to do differently depending on whether pointers fit into uints? Whatever it is, it's usually a bad idea.... – Karl Knechtel Dec 07 '10 at 08:01
  • Pass the pointers to a different process which will call back into the library later (and `unsigned int` can be passed directly, while 64-bit types have to be passed as pointers themselves). – Alexey Romanov Dec 07 '10 at 08:22
  • No they don't. You can pass a 64 bit value in the conventional way - even if your compiling for 32 bit (which you probably aren't if your pointers are 64 bit). – JeremyP Dec 07 '10 at 10:05
  • Yes, they do in this case: see the table in http://www.erlang.org/doc/man/erl_driver.html#driver_output_term – Alexey Romanov Dec 07 '10 at 10:40
  • 2
    What's wrong with always using `intptr_t`? – Christopher Creutzig Dec 08 '10 at 12:26

9 Answers9

16

You just can't do it. sizeof is a compile time operator. #if and #define and preprocessor related. As the preprocessor runs BEFORE the compiler this just won't work. You may, however, be able to find an arcane compiler switch that will allow you to multi pass it (ie preprocess, pretend compile, preprocess, compile) but, in all fairness, I'd give up trying to do what you want. Its not meant to work and, simply, it doesn't.

Your best best is to set such defines as -D commands passed to the compiler. You can statically assert that the ones chosen are correct. This way you just have to set up a few defines externally for a given compile mode (eg PowerPC Release) and so on.

Goz
  • 61,365
  • 24
  • 124
  • 204
  • 4
    The _best_ approach is to use your build system (autotools, etc.) to run checks and make a system-configured file with `#define SIZEOF_POINTER 4` or whatnot, and then use that. – Chris Lutz Dec 07 '10 at 07:58
  • 2
    It's worth noting that in C11 `_Static_assert` will accomplish what OP wants. – chad Aug 18 '16 at 21:07
12

The correct solution to your problem is to use the C99 standard headers:

#include <stdint.h>
#include <inttypes.h>

You only need one of the two because #include <inttypes.h> includes the material from #include <stdint.h>; however, a lot of the material in <inttypes.h> is only relevant to formatted I/O with scanf() and printf().

Given the putative condition:

#if (sizeof(void*) <= sizeof(unsigned int)) // what goes here?
#  define POINTER_FITS_INTO_UINT
#endif

What you seem to be after is known as:

uintptr_t

That is the unsigned integer type that is big enough to hold any pointer (that is, any data pointer in the C standard; POSIX imposes an additional rule that it must also be big enough to hold function pointers too). The type uintptr_t is defined in <stdint.h>.

If you are subsequently going to be printing such values, or raw pointers, you can use the information from <inttypes.h>:

printf("Pointer = 0x%" PRIXPTR "\n", uintptr_value);
printf("Pointer = 0x%" PRIXPTR "\n", (uintptr_t)any_pointer);
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
10

This describes how to fake compile-time assertions in C. The short version is to use switch statements:

#define COMPILE_TIME_ASSERT(pred)            \  
    switch(0){case 0:case pred:;}

If pred evaluates to 0, like a false boolean expression does in C, the compiler will throw an error.

nmichaels
  • 49,466
  • 12
  • 107
  • 135
  • +1. I've used something similar declaring an array with size 0 or -1 (-1 causes a compile error), but I think the use of switch is neater. – qbert220 Oct 19 '12 at 09:02
6

Assuming C99, you could use

#include <limits.h>
#include <stdint.h>

#if UINTPTR_MAX <= UINT_MAX
...

which implies sizeof (void *) <= sizeof (intptr_t) <= sizeof (int) on any sane implementation of the C language.

Christoph
  • 164,997
  • 36
  • 182
  • 240
5

Given that the other answers already explained why sizeof cannot be used with #if, let me provide a simple solution for your case (surprisingly not yet mentioned). Take a look at

https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html#Common-Predefined-Macros.

It mentions several predefined __SIZEOF_XYZ__ macros that actually can be used in preprocessing phase, i.e. also in #if. Assuming unsigned int and int are of same size, your example can be done like this:

#if __SIZEOF_POINTER__ == __SIZEOF_INT__
#define POINTER_FITS_INTO_UINT
#endif
Jens
  • 69,818
  • 15
  • 125
  • 179
stewori
  • 586
  • 4
  • 12
4

Even though the question is tagged C rather than C++, you may find it helpful to know that C++0x defines a mechanism for static assertions which are checked by the compiler, not the preprocessor.

The Wikipedia example is particularly relevant:

static_assert (sizeof(int) <= sizeof(T), "T is not big enough!")
Roddy
  • 66,617
  • 42
  • 165
  • 277
1

Edit

Never mind, as Steve Rowe pointed out, these preprocessor values get set by sizeof as well so we just came full circle.

Since sizeof doesn't evaluate until compile time, you need to rely upon other pre-processor values. Here is how I would do it:

#include <values.h>
#if PTRBITS <= INTBITS
#  define POINTER_FITS_INTO_UINT
#endif
Community
  • 1
  • 1
SiegeX
  • 135,741
  • 24
  • 144
  • 154
  • 1
    How do PTRBITS and INTBITS get set? – Steve Rowe Dec 07 '10 at 07:58
  • 1
    From values.h : #define _TYPEBITS(type) (sizeof (type) * CHAR_BIT) #define INTBITS _TYPEBITS (int) #define PTRBITS _TYPEBITS (char *) I don't know where CHAR_BIT is set but INTBITS and PTRBITS are still sizeof expressions. – Opera Dec 07 '10 at 08:08
  • 1
    @Opera: It just means that INTBITS etc cannot be analyzed by the pre-processor; the macros cannot be used in a `#if` or similar statement. The macros can appear in actual code - but not in preprocessor directives. – Jonathan Leffler Dec 07 '10 at 08:14
0

One way to make sense of this is the concept of Data model (see for example http://sourceforge.net/p/predef/wiki/DataModels/).

There are several data models including LP32 ILP32 LP64 LLP64 ILP64 and on most platforms, the cc frontend command defines the current model (e.g. _ILP32 meaning int, long and pointer are 32bit while _LP64 means long and pointer are 64bit).

Dror Harari
  • 3,076
  • 2
  • 27
  • 25
0

You're confusing two compilation steps here. When you compile a C program, first step is the preprocessor which resolves includes, macros, any line starting by '#'. Then comes the compilation which incidentally evaluates the sizeof expressions.

These are two different binaries and you can't pass that type of information from one to another. You'll have to use system defined macros such as __i386__ or __x86_64__ if you want to figure what architecture you are on and then deduce int and pointer sizes.

Opera
  • 983
  • 1
  • 6
  • 17