Showing posts with label cpp_macro. Show all posts
Showing posts with label cpp_macro. Show all posts

Aug 3, 2026

[C++26] Reflection minute


#include <iostream>

// 1. Define the master list of colors (X Macro)
// This is the single source of truth. Adding a color here 
// automatically updates both the enum and the string converter below.
#define COLORS \
    FUNC(Red)  \
    FUNC(Blue) \
    FUNC(Green)

// 2. Generate the Enum Class
enum class Color {
#define FUNC(name) name,
    COLORS
#undef FUNC
};

// 3. Generate a Helper Function to convert Enum to String using '#' (stringification)
const char* ColorToString(Color color) {
    switch (color) {
#define FUNC(name) case Color::name: return #name;
        COLORS
#undef FUNC
        default: return "Unknown";
    }
}

int main() {
    // Instantiate color variables
    Color r = Color::Red;
    Color b = Color::Blue;
    Color g = Color::Green;

    // Output and verify the mapping works correctly
    std::cout << "Color r is: " << ColorToString(r) << " (Enum value: " << static_cast<int>(r) << ")\n";
    std::cout << "Color b is: " << ColorToString(b) << " (Enum value: " << static_cast<int>(b) << ")\n";
    std::cout << "Color g is: " << ColorToString(g) << " (Enum value: " << static_cast<int>(g) << ")\n";

    return 0;
}

Jul 8, 2022

[C++] inline std::forward impl

Reference:
https://github.com/fmtlib/fmt/blob/b761f1279e2dfb950a7bf9ff7128d6b03b77b013/include/fmt/core.h#L204
https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

{fmt}/core.h itself contains nice coding tips/idea

inline std::forward impl
i.e.
// An inline std::forward replacement.
#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)

Feb 24, 2022

[kernnel][C][C++] ternary conditional operator trick in action

Reference:
Return type of '?:' (ternary conditional operator): https://stackoverflow.com/questions/8535226/return-type-of-ternary-conditional-operator

__is_constexpr() macro is dark magic: https://lore.kernel.org/linux-hardening/20220131204357.1133674-1-keescook@chromium.org/?fbclid=IwAR0Rgg_tGDk0qiEyuDsuZwERITSdstxmU2-bOtadb7iOOCtw3tgOPKEQ5hE

Using ternary conditional operator to get the type we want for a template type is often used in C++.

Here the same idea applies in C macro:

#define __is_constexpr(x) \
	(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
Details:
 - sizeof() is an integer constant expression, and does not evaluate the
   value of its operand; it only examines the type of its operand.
 - The results of comparing two integer constant expressions is also
   an integer constant expression.
 - The use of literal "8" is to avoid warnings about unaligned pointers;
   these could otherwise just be "1"s.
 - (long)(x) is used to avoid warnings about 64-bit types on 32-bit
   architectures.
 - The C standard defines an "integer constant expression" as different
   from a "null pointer constant" (an integer constant 0 pointer).
 - The conditional operator ("... ? ... : ...") returns the type of the
   operand that isn't a null pointer constant. This behavior is the
   central mechanism of the macro.
 - If (x) is an integer constant expression, then the "* 0l" resolves it
   into a null pointer constant, which forces the conditional operator
   to return the type of the last operand: "(int *)".
 - If (x) is not an integer constant expression, then the type of the
   conditional operator is from the first operand: "(void *)".
 - sizeof(int) == 4 and sizeof(void) == 1.
 - The ultimate comparison to "sizeof(int)" chooses between either:
     sizeof(*((int *) (8)) == sizeof(int)   (x was a constant expression)
     sizeof(*((void *)(8)) == sizeof(void)  (x was not a constant expression)


For a conditional expression (?:) to be an lvalue, the second and third operands must be lvalues of the same type.
This is because the type and value category of a conditional expression is determined at compile time and must be appropriate whether or not the condition is true.
If one of the operands must be converted to a different type to match the other than the conditional expression cannot be an lvalue as the result of this conversion would not be an lvalue (but a r-value).

Thus:

OK:
int x = 1;
int y = 2;
(x > y ? x : y) = 100; // l-value on the left side of =

Not OK:
int x = 1;
long y = 2;
(x > y ? x : y) = 100; // type conversion to long as r-value type

Nov 2, 2018

[CppCon 2018] The Nightmare of Initialization in C++ - Nicolai Josuttis


Interestingly RVO (Return Value Optimization) has become a hot topic in C++17, not only Nicolai talks about it, Arthur O'Dwyer also has a talk in CppCon2018 talking about RVO.

Years back when I first read of RVO is, again, from the classic book written by Stanley B. Lippman, Inside the C++ Object Model. RVO used to be an option for compiler, but now is mandated in C++17.


Initialization in C++
Well, you could see this mocking video about C++'s initialization on the web :-)


RVO works for constructor
// RVO works for constructor.
auto a = std::atomic<int>{9};
auto r = std::array{};



C++17's prvalue:
prvalue can temporary materialization conversion' to
xvalue.


Does compiler always warns while doing a narrow type
conversion?
Not really for template:
[C++11/14] constant expression value can convert to smaller size data structure


C++17 relaxed Enumeration Initialization
Initialize enumerations with integral values:

enum class Test { a, b};
Test te1{0}; // ok in C++17

enum Int : unsigned long long{};
Int ie1{42}; // ok in C++17

enum Flag {b1=1, b2=2};
Flag f3{0}; // error!

std::byte b{0b111'111}; // ok in C++17

Rule of thumb:
Make sure either both default constructor and one argument/initialization list constructor explicit or both non-explicit.
If not:
vector<int> v1 = {1 , 2}; // ok
vector<int> v2 = {1}; // ok
vector<int> v2 = {}; // error! in C++11, fixed in C++14


Aggregates:
  • C
    • Structs or arrays (types for multiple members, not union)
  • C++98/03: class or array with:
    • no user-declared constructors
    • no private or protected non-static data members
    • no base classes
    • no virtual functions
  • C++11/14: class or array with:
    • no user-provided constructors
    • no private or protected non-static data members
    • no base classes
    • no virtual functions
  • C++17: class or array with:
    • no user-provided, explicit, or inherited constructors
    • no private or protected non-static data members
    • no virtual, private, or protected base classes
    • no virtual functions
  • std::array<> is an aggregate,
    i.e without initialize value is an UB.
Reference:
[golang][c++] padding


assert:
Beware about comma, since assert is a macro.
This is mentioned in Davide Di Gennaro's
Advanced Metaprogramming in Classic C++
assert( c == std::complex(0,0)); // ok
assert( c == std::complex{0,0}); // error
assert(( c == std::complex{0,0})); // ok



Reference:

May 9, 2018

[macro] ## parse tokens

https://stackoverflow.com/questions/49937805/c-macro-doesnt-work-after-operator

detailed answer:
https://stackoverflow.com/a/41691582

GCC Doc:
https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

GCC Doc, Macro:
https://gcc.gnu.org/onlinedocs/cpp/Macros.html#Macros


# : produce string type.
## : concatenate 2 operand into single text, not string type.


The token-pasting operator (##) is used to concatenate two tokens into a single valid token.


When write
x->##type##_value();


The first processed token is x.

The next token is formed by concatenating the token -> with type, since type is a, the result of the concatenation is ->a, which ought to be a valid token, but is not.

Hence, you get the error: pasting formed '->a', an invalid preprocessing token.

To fix this, just write
x->type##_value();

This way:
  • The first token parsed is x.
  • The next token parsed is ->.
  • The next token is formed by concatenating the token type (which becomes a) with the token _value. This gives a_value, which is a valid token.
  • The next token is (.
  • The next token is ).
  • The last token is ;.

Jan 23, 2016

[likely or unlikely] a easy misleading.

Reference:
Using likely() and unlikely()
Clang ignores branch predictor hints using __builtin_expect
#define likely(x) __builtin_expect ((x), 1)
#define unlikely(x) __builtin_expect ((x), 0)

The rule of the thumb is: Mark branch that you want to be executed quickly as "likely" and the other branch as "unlikely".
#ifdef FOO
#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)
#else
#define likely(x)       x
#define unlikely(x)     x
#endif
 
volatile int x,y,z;
int array[100];
 
// switch like
char const* b(int e) {
    if (likely(e == 0))
    {
        // for(int i=0; i<100;i++)
        //     array[i]=x;    
        return "0";
    }    
    else if (e == 1)
    {
        for(int i=0; i<100;i++)
            array[i]=y;
        return "1";
    }
    else
    {
        for(int i=0; i<100;i++)
            array[i]=z;
        return "f";
    }
}

Aug 20, 2015

[C++][Enum Macro]

from reddit:
----------
#define STRONG_ENUM(Name, ...) \
    class Name { \
    public: \
        enum Name##_ { \
            __VA_ARGS__ \
        }; \
    public: \
        Name(Name::Name##_ v) : mValue(v) {} \
        operator Name::Name##_() const { return mValue; } \
        Name& operator=(Name::Name##_ v) { \
            mValue = v; \
            return *this; \
        } \
    private: \
        Name::Name##_ mValue; \
    }

usage:
STRONG_ENUM(Color,
    Red,
    Green,
    Blue
);

const char* tostring(Color c) {
    switch(c) {
    case Color::Red:
        return "red";
    case Color::Green:
        return "green";
    case Color::Blue:
        return "blue";
    }
}

Color c = Color::Red;
std::cout << tostring(c) << std::endl;

Nov 27, 2014

[C++11] something about auto... in generic lambda

Generic lambda inconsistency? C++11's six dots
void f(int...);
// equivalent to
void f(int, ...);
Likewise..
[](auto&&...) { return 42; };
// equivalent to
[](auto&& , ...) { return 42; };
This is what allows the funny quirk of 6 periods:
template<typename... Args>
void f(Args&&......) { }
// equivalent to
template<typename... Args>
void f(Args&&..., ...) { }
So, what's the workaround? easy:
[](auto&&... t) { return 42; };

Reference:
https://en.cppreference.com/w/cpp/language/variadic_arguments
In the C programming language, at least one named parameter must appear before the ellipsis parameter, so printz(...); is not valid.
In C++, this form is allowed even though the arguments passed to such function are not accessible, and is commonly used as the fallback overload in SFINAE, exploiting the lowest priority of the ellipsis conversion in overload resolution.

This syntax for variadic arguments was introduced in 1983 C++ without the comma before the ellipsis.

When C89 adopted function prototypes from C++, it replaced the syntax with one requiring the comma.

For compatibility, C++98 accepts both C++-style f(int n...) and C-style f(int n, ...)

Sep 8, 2012

[Macro][NOTE]

Macros
Variadic Macros
Tips on writing C macros

FIX:
Use ntohs(3) instead of ::ntohs(3).

The alternative fix

Add the following line after your includes:

#undef ntohs
Excerpt from "Tips on writing C macros"

RULE 1: Always write your multiline macros using this pattern:
    #define MYMACRO \
    do { \
       macro definition here \
    } while (0)
The only trouble you might get with this, is some smart-a$$ code analyzer screaming about a 'constant expression in do-while condition'. That's usually easy to turn off by adding some comment-directive to your macro definition. Just make sure you only use /*C-style comments*/ in macros ;-) RULE 2: Always surround macro arguments with parentheses inside the macro body.
    #define MYMACRO(a,b,c) \
    do { \
       (a) = (b) + (c); \
       (b) = (c)*2; \
    } while (0)
it's better to use
#define REGISTER_CONTEXT_FACTORY_FUNCTION(fn...)
then
REGISTER_CONTEXT_FACTORY_FUNCTION((lambda));
1) Developer Experience (Ergonomics):
Forcing developers to write extra double parentheses (( ... ))
is awkward and prone to human error.
2) Hard-to-Debug Compiler Errors:
If a developer forgets the extra parentheses, they get very
confusing preprocessor errors (e.g., "macro passed 2 arguments but takes 1"),
which are difficult to trace back to a missing set of parentheses.
3) Syntactic Cleanliness:
Using fn... allows the registration to look like a native C++
function invocation (REGISTER_CONTEXT_FACTORY_FUNCTION([](...) { ... })),
keeping the boilerplate minimal and clean.


RULE 3: Keep your macros SHORT. Don't write 50-line macros, because when the time comes to chase down some bug you will soon find out that the only debugger that could step into macro definitions (SoftICE) is out of business. To the best of my knowledge, even WinDBG, the Windows kernel debugger, cannot step into macros. So, keep'em short. RULE 4: Be very careful when trying to use macros for speed optimizations (i.e. save a function call). I have seen even senior programmers get it wrong, because they didn't realize that passing MyArray[x+3] as a macro argument would lexically copy this expression in multiple locations in the macro expansion, causing the generated code to needlessly evaluate MyArray[x+3] again and again and again. Always have someone else, preferably more experienced than yourself, check these 'optimizations' with you.