15

I have a macro that uses GCC's typeof to create a variable of the same type of a macro argument. The problem is: if that argument has const type, the variable created inside the macro is const and I can't use it. For instance:

#include <stdio.h>

#define DECR(x) ({typeof(x) y; y = x; y--; y;})

int main(void)
{
    const int v = 5;
    printf("%d\n", DECR(v));
    return 0;
}

Compilation gives:

$ cc    -c -o t.o t.c
t.c: In function 'main':
t.c:9:2: error: assignment of read-only variable 'y'
t.c:9:2: error: decrement of read-only variable 'y'
make: *** [t.o] Error 1

Is there a way to copy the typeof a value and un-const it?

2
  • 2
    For your example, there's no need to create a variable; you could use #define DECR(x) ((x) - 1). If that doesn't meet your requirements, you might want to update your question so it's clear that you really need the variable. Commented Aug 5, 2013 at 17:37
  • I know, it's just an example. In the real code I have a for loop. Commented Aug 5, 2013 at 18:39

6 Answers 6

11

If you don't mind the possible arithmetic promotion you can do this:

#define DECR(x) ({typeof(x + 0) y; y = x; y--; y;})

The trick is that the expression for typeof is x + 0, which is a r-value, and so the l-value-constness (which is what you want to avoid) is lost.

The same trick can be done with 1 * x, but curiously enough, +x and -x don't work.

Sign up to request clarification or add additional context in comments.

2 Comments

@aaronman: Not just integer types. Also floating-point and pointers.
@EricPostpischil I guess I'll just stop trying to answer C questions
4

This is a rather late answer, but if you don't mind using more GCC extensions you can do this like this (building upon a previous answer somewhat).

#define UNCONST_HAX_(TYPE) ({TYPE _tmp_macro_var_; _tmp_macro_var_;})
#define UNCONST(x)                                                      \
    __typeof__(_Generic((x),                                            \
            signed char:              UNCONST_HAX_(signed char),        \
            const signed char:        UNCONST_HAX_(signed char),        \
            unsigned char:            UNCONST_HAX_(unsigned char),      \
            const unsigned char:      UNCONST_HAX_(unsigned char),      \
            short:                    UNCONST_HAX_(short),              \
            const short:              UNCONST_HAX_(short),              \
            unsigned short:           UNCONST_HAX_(unsigned short),     \
            const unsigned short:     UNCONST_HAX_(unsigned short),     \
            int:                      UNCONST_HAX_(int),                \
            const int:                UNCONST_HAX_(int),                \
            unsigned:                 UNCONST_HAX_(unsigned),           \
            const unsigned:           UNCONST_HAX_(unsigned),           \
            long:                     UNCONST_HAX_(long),               \
            const long:               UNCONST_HAX_(long),               \
            unsigned long:            UNCONST_HAX_(unsigned long),      \
            const unsigned long:      UNCONST_HAX_(unsigned long),      \
            long long:                UNCONST_HAX_(long long),          \
            const long long:          UNCONST_HAX_(long long),          \
            unsigned long long:       UNCONST_HAX_(unsigned long long), \
            const unsigned long long: UNCONST_HAX_(unsigned long long), \
            float:                    UNCONST_HAX_(float),              \
            const float:              UNCONST_HAX_(float),              \
            double:                   UNCONST_HAX_(double),             \
            const double:             UNCONST_HAX_(double),             \
            long double:              UNCONST_HAX_(long double),        \
            const long double:        UNCONST_HAX_(long double)         \
    ))

And it could be used as follows:

#define DECR(x) ({UNCONST(x) y; y = x; y--; y;})

Yes, it is pretty ugly.

Comments

3

You could use a C11 _Generic selection to map from const to non-const type:

#define DECR_(t, x) ({ t y = (x); --y; y; })
#define DECR(x) _Generic((x),                     \
    int: DECR_(int, (x)),                         \
    const int: DECR_(int, (x)),                   \
    long: DECR_(long, (x)),                       \
    const long: DECR_(long, (x)),                 \
    unsigned int: DECR_(unsigned int, (x)),       \
    const unsigned int: DECR_(unsigned int, (x)), \
    long long: DECR_(long long, (x)),             \
    const long long: DECR_(long long, (x)))

Although it involves a LOT of typing, even if you only need to cover integral types. C11 is also far from being widely available these days. Live example at Coliru.

3 Comments

I will admit yours is probably the best answer
@aaronman Ironically, I think although it does what the OP asked for it's still basically useless due to requiring C11. And having to manually specify all the types is awkward at best.
This is why I use cpp
2

Is it possible to un-const typeof in gcc pure C?

I don't thing so, but this will work:

#define DECR(x) __extension__({__typeof__(x) y = x - 1; y;})

Note that __extension__ is used for disable ISO C forbids braced-groups within expressions[-pedantic] warning.

3 Comments

I'm assuming this is gcc specific though
The example I used is not real code. In the code, I have a for loop, so this answer is not applicable.
Show us a practical example of the for loop, maybe we can found an alternative.
1

__auto_type will remove const and volitale. Like this:

#include <stdio.h>

int main()
{
    const int x=3;
    __typeof__(({__auto_type y=x; y;})) z=1;
    z++;
    printf("%d %d\n", x, z);
  return 0;
}

You can change your code to:

#define __auto_typeof(x) __typeof__(({__auto_type y=x; y;}))

#include <stdio.h>

#define DECR(x) ({__auto_typeof(x) y; y = x; y--; y;})

int main(void)
{
    const int v = 5;
    printf("%d\n", DECR(v));
    return 0;
}

Comments

0

There is no standard way in c to modify a const variable or remove the specifier from an existing one.

27 Comments

This is not an answer. OP is asking for an equivalent of the C++ metafunction std::remove_cv<decltype(x)>::type in C with GNU extensions.
@Casey: Ok, do you know of such a construct in C? If not, then this is the answer.
@EdS.: No, this is not the answer. The question is “Is there a way to copy the typeof a value and un-const it?” If there is no way, then the answer is “No.”
@aaronman You are knowingly presenting code with undefined behavior as an answer. What is there to explain?
That should be your answer, then: "There is no standard way in c to modify a const variable." The rest is just causing confusion at best.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.