14

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?

Penz
  • 5,428
  • 5
  • 31
  • 28
  • 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. – Keith Thompson Aug 05 '13 at 17:37
  • I know, it's just an example. In the real code I have a `for` loop. – Penz Aug 05 '13 at 18:39

6 Answers6

10

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.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
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.

Roflcopter4
  • 679
  • 6
  • 16
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.

Casey
  • 41,449
  • 7
  • 95
  • 125
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.

David Ranieri
  • 39,972
  • 7
  • 52
  • 94
0

__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;
}
untitled
  • 181
  • 6
-1

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

aaronman
  • 18,343
  • 7
  • 63
  • 78
  • 2
    This is not an answer. OP is asking for an equivalent of the C++ metafunction `std::remove_cv::type` in C with GNU extensions. – Casey Aug 05 '13 at 17:17
  • 1
    @Casey: Ok, do you know of such a construct in C? If not, then this *is* the answer. – Ed S. Aug 05 '13 at 17:19
  • 1
    @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.” – Eric Postpischil Aug 05 '13 at 17:33
  • @EricPostpischil I am attempting to give him a way to safely modify the variable even if it is still const – aaronman Aug 05 '13 at 17:34
  • @aaronman: This answer does not accomplish that either. The behavior of attempting to modify an object defined with `const` is not defined by the C standard (or by GCC, as far as I know). Doing it through a pointer does not avoid that. Adding `volatile` does not avoid that. – Eric Postpischil Aug 05 '13 at 17:36
  • @aaronman a) There is no way to safely modify a const variable and b) He doesn't want to modify the variable. His code does not modify the variable even if it isn't const. – sepp2k Aug 05 '13 at 17:37
  • @sepp2k his problem is that he creates a const variable, and can't modify it, so if he can't make that variable non-const modifying the variable anyway is how to solve his problem, my volatile solution is still likely undefined behavior but it does seem to work – aaronman Aug 05 '13 at 17:39
  • @EricPostpischil i'm not suggesting to use this type of code at all but it is an interesting example, also if you notice his macro cannot even be used more than once in the same scope because the variable name will be the same – aaronman Aug 05 '13 at 17:41
  • @aaronman: The scope of the name is the containing block, which is the `({…})` construct. The macro may be reused. And the code in this answer is not interesting; it is broken and should not be used. – Eric Postpischil Aug 05 '13 at 17:45
  • http://embeddedgurus.com/barr-code/2012/01/combining-cs-volatile-and-const-keywords/ @EricPostpischil I don't believe that it is broken – aaronman Aug 05 '13 at 17:46
  • @EricPostpischil also all of you are being overly critical of my answer because originally I just stated that it is impossible to do with out UB (though apparently it can be done with extensions) – aaronman Aug 05 '13 at 17:49
  • @aaronman: That is a different situation. The object is modified by **another** process, outside the C implementation (e.g., hardware). The `const` tells the C implementation that **this program** will not modify the object, and the `volatile` says **somebody else** might. The behavior of this combination is defined by the C standard. When a program defines an object to be `const`, then that program may not modify the object. – Eric Postpischil Aug 05 '13 at 17:51
  • @EricPostpischil just because the modification is coming from somewhere else doesn't mean the concept isn't the same. And as I said before you are being overly critical if it makes you happy I will remove the volatile part of my answer, I'm assuming you are the DV'r – aaronman Aug 05 '13 at 17:53
  • @aaronman: Yes, it does mean the concept is not the same. A program may not modify its own object defined with a const-qualified type, per C 2011 (N1570) 6.7.3 6: “If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.” An external agent may modify an object that has volatile-qualified type, per 6.7.3 7: “An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects.” … – Eric Postpischil Aug 05 '13 at 17:58
  • … A program that modifies a `const` object that it defined violates the first of these rules. The situation from the example you linked to violates neither of these rules. Therefore, they are different situations. – Eric Postpischil Aug 05 '13 at 17:58
  • @EricPostpischil I see how it would break the first rule, but then the second rule _to me_ leaves it up for debate as to wether the volatile keyword exempts a variable from the first rule, I don't want to argue with you anymore as you clearly have a better knowledge of C than I, but I am going to post this debate as a question you can answer it if you want. – aaronman Aug 05 '13 at 18:04
  • http://stackoverflow.com/questions/18064722/modifying-a-const-variable-with-the-volatile-keyword here's the link if you want – aaronman Aug 05 '13 at 18:06
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/34861/discussion-between-aaronman-and-eric-postpischil) – aaronman Aug 05 '13 at 18:08
  • @Casey I would really appreciate if you would better explain why you downvoted – aaronman Aug 05 '13 at 20:21
  • 1
    @aaronman You are knowingly presenting code with undefined behavior as an answer. What is there to explain? – Casey Aug 05 '13 at 20:23
  • @Casey so what I wrote write under it that it is undefined behavior, there is no standard way in c to modify a const variable – aaronman Aug 05 '13 at 20:23
  • 1
    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. – Casey Aug 05 '13 at 20:25
  • @Casey that is my answer now – aaronman Aug 05 '13 at 20:26
  • @EricPostpischil: I must have misread this answer or something. When I first read it I could have sword it said no more than "No, there is no way to do this, and results in UB". – Ed S. Aug 05 '13 at 21:32
  • @EdS. as you can see from the comments and the edit history it's been through a lot – aaronman Aug 05 '13 at 21:35
  • @aaronman: Haha, yes, I see that. I just came back from getting an oil change... – Ed S. Aug 05 '13 at 21:37
  • @EdS. if you look through the comments I actually had such a large discussion that I decided to post a question about it – aaronman Aug 05 '13 at 21:39