Files
carbon-lang/docs/design/interoperability/macros.md
T
341069fdcf Add design doc for proposal #6676: Importing C/C++ object-like macros (#7291)
Assisted-by: Gemini via Antigravity

---------

Co-authored-by: Josh L <josh11b@users.noreply.github.com>
Co-authored-by: Geoff Romer <gromer@google.com>
2026-06-04 20:07:42 +00:00

6.1 KiB

Importing C/C++ macros

Table of contents

Overview

C/C++ object-like macros are frequently used in APIs of standard and low-level C++ libraries to define constants (such as error codes in <errno.h>). To support seamless interoperability, an object-like C/C++ macro that evaluates to a constant expression is imported into Carbon as a constant.

For example:

C++:

#define BUFFER_SIZE 4096

Carbon:

// Cpp.BUFFER_SIZE is imported as a value of type i32 with a value of 4096.
let a: i32 = Cpp.BUFFER_SIZE;

Details

Namespace

Imported C++ macros are evaluated in the global Cpp namespace and are accessible under that prefix (for example, Cpp.BUFFER_SIZE).

Constant type

The type of the imported constant is deduced by Clang by evaluating the constant expression, and then mapped to a Carbon type following the Carbon <-> C++ type mapping rules.

Constant value

The value of the constant is deduced by evaluating the tokens of the macro's replacement list as a C++ constant expression.

Supported constant expressions

The replacement list in the object-like macro expanding to a constant expression can contain:

  • Operators: arithmetic: +, -, *, /; bitwise: |, &, ^, <<, >> ; logical: ||, &&; comparison: <, >, <=, >=, ==; casts etc, with arbitrary number of operands.

    For example:

    #define ADDITION 1+2+3
    

    However, note that this macro behaves differently in Carbon when used inside an expression. The following C++ program prints 7, since the macro is expanded before the multiplication operation; 2 * 1 + 2 + 3 is evaluated as (2 * 1) + 2 + 3:

    #include <iostream>
    
    #define ADDITION 1+2+3
    
    int main() {
    std::cout << (2 * ADDITION) << '\n';
    }
    

    While the following Carbon program prints 12, since Cpp.ADDITION is treated as a constant with value 6:

    import Core library "io";
    
    import Cpp inline "#define ADDITION 1+2+3";
    
    fn Run() {
    Core.Print(2 * Cpp.ADDITION);
    }
    
  • Chained macros: macros that expand to other macros which evaluate to constants.

    For example:

    #define VALUE 123
    #define MY_VALUE VALUE
    
  • Enum constants and constexpr variables: if a macro's replacement list refers to a named constant, such as an enum constant or a constexpr variable, it is imported as an alias rather than as a literal value. This allows Carbon to preserve the specific type of the constant (such as Color in the example below). In the case of constexpr variables, importing as an alias also preserves addressability (that the constant is an lvalue), which would be lost if only the value were imported.

    For example:

    C++:

    enum class Color { Red = 1, Green = 2 };
    #define GREEN_COLOR Color::Green
    
    constexpr int kValue = 123;
    #define VALUE kValue
    

    Carbon:

    // Cpp.GREEN_COLOR is an alias to Cpp.Color.Green which has a type Cpp.Color.
    let b: Cpp.Color = Cpp.GREEN_COLOR;
    
    // Cpp.VALUE is an alias to kValue.
    let a: i32 = Cpp.VALUE;
    

Macros are evaluated in the global namespace (for example Cpp.VALUE).

Future work: Evaluating in a child namespace (Cpp.SomeNamespace.VALUE) may also be possible.

Empty macros

Macros without a replacement list are not imported into Carbon. They do not have a Carbon equivalent.

#define EMPTY

Implementation

  1. Name lookup: When a C++ macro name is encountered in Carbon, it is looked up first, before other names. Following C++ rules, this ensures that the macro is found even if there is a non-macro entity (such as a variable) with the same name.

  2. Macro import: If an eligible macro is found, the compiler (effectively) attempts to generate a C++ helper declaration and imports it if that succeeds:

    constexpr inline decltype(auto) __carbon_import_MY_MACRO = (MY_MACRO);
    

    This delegates parsing and type/value evaluation to Clang.

Future work

Whether Carbon will support other macro forms is still to be determined:

Alternatives considered

References