0

I am building some generic things in C.

Here is the code:

// main.c
#include <stdio.h>

#define T int;
#include "test.h"

int main()
{

    return 0;
}

// test.h

#define _array_is_pointer(T) (                \
    {                                         \
        T _value;                             \
        __builtin_classify_type(_value) == 5; \
    })

#ifdef T
#if _array_is_pointer(T)

struct array_s
{
    T *items;
}

void array_push(struct array_s * array, T value)
{
    // push method for pointer.
}

#else

struct array_s
{
    T *items;
}

void array_push(struct array_s * array, T value)
{
    // push method for non-pointer.
}

#endif
#endif

** edited: add more code in test.h **

I would like the preprocessor runs different code when T is pointer or non-pointer.

But I got an error token "{" is not valid in preprocessor expressions.

Is it possible to do that?

kelvinwong
  • 103
  • 5
  • 3
    `#define T int;` probably you don't want `;` here – Iłya Bursov Oct 07 '22 at 16:21
  • 3
    `__builtin_classify_type` is a builtin _function_, so the preprocessor knows _nothing_ of this. You have to use `if/else` and/or `switch/case` to handle this. (e.g.) `#define OPERATE(T) do { T _value; switch (__builtin_classify_type(_value)) { case 5: do_pointer(); break; default: do_non_pointer(); break; } } while (0)` But, what do you want to do? The use of this function is a bit dicey. More context would help. And, there may be better ways to achieve what you want. – Craig Estey Oct 07 '22 at 16:25
  • @CraigEstey Thanks for reply. I just add more code in test.h – kelvinwong Oct 07 '22 at 17:02

3 Answers3

2

I would like the preprocessor runs different code when T is pointer or non-pointer.

Is it possible to do that?

No, it is not possible. Preprocessor is not aware of types.

If you really want this, pass a mark if T is a pointer or not as a separate macro.

#define T  int*
#define T_IS_A_POINTER  1
#include "test.h"

Or have separate calls:

#define T  int*
#include "test_a_pointer.h"

#define T  int
#include "test_not_a_pointer.h"
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
1

The preprocessor doesn't know whether T is a pointer, because preprocessing happens before semantic analysis of the program. All the preprocessor sees are tokens; it knows that 42 is a number and take42, but that's it. The only definitions it knows about are preprocessor #defines.

Moreover, in C, functions --even builtin constant functions like sizeof and __builtin_classify_type-- cannot be evaluated by the preprocessor. The preprocessor cannot evaluate block expressions either, but there wouldn't be much point so it has no idea what a variable is and thus doesn't need declarations. The only identifier you can use in an #if preprocessor conditional are macro definitions which expand to integer constants (or entire expressions containing only arithmetic operations on integer constants).

There is the _Generic construct introduced in C11, which allows you to generate different expressions based on the type of a controlling expression. But it can only be used to generate expressions, not declarations, so it's probably not much help either.

rici
  • 234,347
  • 28
  • 237
  • 341
-1

There is no issue while writing multi-line code-snippet in

#define _array_is_pointer(T) (                \
    {                                         \
        T _value;                             \
        __builtin_classify_type(_value) == 5; \
    })

But, as you have know, the first step done before passing the code to compiler is to create an Expanded source code. In this step, all the five lines woud be pasted whereever you would have written _array_is_pointer(T) and hence resulting code would have :

#if (                
        {                                         
            T _value;                             
            __builtin_classify_type(_value) == 5; 
        })

and here is a blunder. One can not write multiple lines like this in if clause, nor you could do this using {}. And hence, you got the error token "{" is not valid in preprocessor expressions.

Hence, you would have to write a single expression to in if clause preprocessor.

  • The definition of `_array_is_pointer` is only one line, because the lines are spliced together by the trailing backslashes. That has nothing to do with the error. You're free to spread a macro definition over as many physical lines as you like provided that it is only one logical line. Anyway, whitespace is not part of a macro's replacement list. Macro substitution is an operation on tokens, not characters. – rici Oct 07 '22 at 17:53
  • @rici, read again. My answer is also same. I said that there is no issue in definition of _array_is_pointer. The issue raises when the code gets expanded, as kelvinwong is tring to put multiple lines in a if condition, which is illega. And you can't say that T _value; __builtin_classify_type(_value) == 5; would be treated as a single line by compiler. – Priyas Paulzagade Oct 08 '22 at 01:46
  • yes, I can, because it is. Take a look at 5.1.1.2 of the C standard, which defines the phases of processing. Step 2 is "Each instance of a backslash character (\) immediately followed by a new-line character is deleted, **splicing physical source lines to form logical source lines**." Preprocessing doesn't happen until step 4, so when the preprocessor sees that #define, it's a single logical line, and that's how it's treated when the macro is expanded. – rici Oct 08 '22 at 02:32
  • If you meant that it's not an expression, that's kind of true. (But that's very different from saying "multiple lines".) Gcc treats the block as a single expression, as an extension to C. You can use that block in many places where an expression is required. But Gcc does not allow block expressions in `#if` preprocessor directives; those allow only a subset of the expression grammar. (You can't use string literals, for example.) – rici Oct 08 '22 at 02:38
  • But note that if you wrote all that in an `if` statement, rather than a `#if` preprocesor directive, it would be fine. (In GCC or Clang, at least.) In case you've never seen that syntax before, you can read the [GCC documentation](https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html) – rici Oct 08 '22 at 02:52
  • Yes, I talked about condition expression in #if. But as it is valid, please mention, which statement would be treated as expression in if condition. – Priyas Paulzagade Oct 08 '22 at 04:00
  • `if ( ({ T _value; __builtin_classify_type(_value) == 5; }) ) puts("T has type 5");` (assuming T is defined at that point). Here `{...}` is a statement (a block statement), but surrounded by parentheses it becomes an expression. The value of the block is the last expression. I'm not actually sure that's what you were asking. The GCC documentation is not very long, though. – rici Oct 08 '22 at 04:13
  • This is a bit more readable, maybe: https://godbolt.org/z/Yqrv8roaE – rici Oct 08 '22 at 04:14