QLM
QLM

Reputation: 47

C Preprocessor macros extension concatenation

Suppose I would like to use the C Preprocessor to define a family of function, for which only a parameter type changes. Let's take for example allocation functions with parameters:

#include <stdint.h>
#include <stdio.h>

#define type_vector(t)                                              \
t * t##vector(int32_t nl, int32_t nh)                               \
{                                                                   \
    t * v = NULL; /* malloc(...) */                                 \
    return v;                                                       \
}

type_vector(int8_t)
type_vector(int32_t)
type_vector(int64_t)

int main() {
    int32_t * p;
    p = int32_tvector(10, 12);
    return 0;
}

This works fine.

Now, suppose I would like to have shorter names for type in the function names: i8 instead of int8_t, i32 instead of int32_t, etc. I do not want to add a second parameter to the macro function declaration, but instead have a single definition of "short" type names. Basically, the first thing I wrote was:

#define sn_int8_t   i8
#define sn_int32_t  i32
#define sn_int64_t  i64

#define sn(t)          sn_##t

#define type_vector(t)                                              \
t * sn(t)##vector(int32_t nl, int32_t nh)                           \
{                                                                   \
    t * v = NULL; /* malloc(...) */                                 \
    return v;                                                       \
}

type_vector(int8_t)
type_vector(int32_t)
type_vector(int64_t)

int main() {
    int32_t * p;
    p = i32vector(10, 12);
    return 0;
}

However, this does not compile, giving the following error:

error: pasting ")" and "vector" does not give a valid preprocessing token
 t * sn(t)##vector(int32_t nl, int32_t nh)                           \

As far as I understood, the problem is due to having the ")" and the "#" side by side. So I did the following:

#define sn_int8_t(f)   i8##f
#define sn_int32_t(f)  i32##f
#define sn_int64_t(f)  i64##f

#define sn2(t,f)       sn_##t(f)

#define type_vector(t)                                              \
t * sn2(t,vector)(int32_t nl, int32_t nh)                           \
{                                                                   \
    t * v = NULL; /* malloc(...) */                                 \
    return v;                                                       \
}

type_vector(int8_t)
type_vector(int32_t)
type_vector(int64_t)

int main() {
    int32_t * p;
    p = i32vector(10, 12);
    return 0;
}

And this works fine again.

Now, suppose I would like to add a prefix to the function name, e.g. "remote_".

Simply putting

#define type_vector(t)                                              \
t * remote_##sn2(t,vector)(int32_t nl, int32_t nh)                  \
{                                                                   \
    t * v = NULL; /* malloc(...) */                                 \
    return v;                                                       \
}

does not work, as sn2 is not expanded. So I tried this:

#include <stdint.h>
#include <stdio.h>

#define sn_int8_t(f)   i8##f
#define sn_int32_t(f)  i32##f
#define sn_int64_t(f)  i64##f

#define sn2(t,f)       sn_##t(f)

#define short_name(n,t,f) n##_##sn2(t,f)

#define remote_type_vector(t)                                       \
t * short_name(remote,t,vector)(int32_t nl, int32_t nh)             \
{                                                                   \
    t * v = NULL;                                                   \
    return v;                                                       \
}

remote_type_vector(int8_t)
remote_type_vector(int32_t)
remote_type_vector(int64_t)

int main() {
    int32_t * p;
    p = remote_i32vector(10, 12);
    return 0;
}

Basically, this does not work because the macro sn2 is not expanded, giving me declarations like:

int32_t * remote_sn2(int32_t,vector)

Is there any way to do that?

Note: I do not want to use C++ templates.

Upvotes: 2

Views: 208

Answers (1)

Tom Karzes
Tom Karzes

Reputation: 24102

Here's a unified solution that should work for all of your needs:

#define sn_int8_t(p,s)  p##i8##s
#define sn_int32_t(p,s) p##i32##s
#define sn_int64_t(p,s) p##i64##s

#define sn2(t,p,s)       sn_##t(p,s)

#define type_vector(t)                                              \
t * sn2(t,,vector)(int32_t nl, int32_t nh)                          \
{                                                                   \
    t * v = NULL; /* malloc(...) */                                 \
    return v;                                                       \
}

#define remote_type_vector(t)                                       \
t * sn2(t,remote,vector)(int32_t nl, int32_t nh)                    \
{                                                                   \
    t * v = NULL;                                                   \
    return v;                                                       \
}

type_vector(int8_t)
type_vector(int32_t)
type_vector(int64_t)

remote_type_vector(int8_t)
remote_type_vector(int32_t)
remote_type_vector(int64_t)

Upvotes: 0

Related Questions