Skip to content

宏与预处理器

源:Interoperability with C - Macros

C 宏是预处理器指令,cinterop 工具对宏的处理有特定规则。理解这些规则对正确使用 C 库至关重要。

宏的分类

常量宏(自动支持)

简单常量宏会自动转换为 Kotlin 属性

c
// config.h
#define MAX_BUFFER_SIZE 1024
#define PI 3.14159265359
#define VERSION_STRING "2.1.0"
#define ENABLE_LOGGING 1
kotlin
@OptIn(ExperimentalForeignApi::class)
import config.*

fun main() {
    println(MAX_BUFFER_SIZE)  // 1024
    println(PI)               // 3.14159265359
    println(VERSION_STRING)   // "2.1.0"
    println(ENABLE_LOGGING)   // 1
    
    // 类型推断
    val bufferSize: Int = MAX_BUFFER_SIZE
    val pi: Double = PI
    val version: String = VERSION_STRING
}

类型推断规则

cinterop 能够推断以下类型的常量宏:

C 宏定义Kotlin 类型示例
整数字面量Int#define COUNT 42
长整数字面量Long#define BIG 10000000000L
浮点字面量Double#define RATIO 1.5
字符串字面量String#define NAME "foo"
字符字面量Byte#define CH 'A'

不支持的宏

函数式宏(需要手动包装)

函数式宏无法自动转换

c
// math_macros.h
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define CLAMP(x, low, high) (MAX(low, MIN(x, high)))

这些宏在 Kotlin 中不可用。尝试调用会导致编译错误。

包装函数式宏

方法一:def 文件内联包装

properties
# math.def
headers = math_macros.h
package = math

---

// 在def文件中添加包装函数
static inline int square_wrap(int x) {
    return SQUARE(x);
}

static inline int max_wrap(int a, int b) {
    return MAX(a, b);
}

static inline int min_wrap(int a, int b) {
    return MIN(a, b);
}

static inline int clamp_wrap(int x, int low, int high) {
    return CLAMP(x, low, high);
}
kotlin
// Kotlin 使用
@OptIn(ExperimentalForeignApi::class)
import math.*

fun testMacros() {
    println(square_wrap(5))        // 25
    println(max_wrap(10, 20))      // 20
    println(min_wrap(10, 20))      // 10
    println(clamp_wrap(15, 0, 10)) // 10
}

方法二:独立包装头文件

对于大量宏,创建专用包装文件:

c
#ifndef MACRO_WRAPPERS_H
#define MACRO_WRAPPERS_H

#include "math_macros.h"

// 数学宏包装
static inline int square(int x) {
    return SQUARE(x);
}

static inline double square_double(double x) {
    return SQUARE(x);
}

// 泛型宏的类型特化版本
static inline int int_max(int a, int b) {
    return MAX(a, b);
}

static inline double double_max(double a, double b) {
    return MAX(a, b);
}

// 复杂宏
static inline int clamp_int(int value, int min_val, int max_val) {
    return CLAMP(value, min_val, max_val);
}

#endif
properties
headers = math_macros.h macro_wrappers.h
headerFilter = macro_wrappers.h
package = math

# 只导入包装函数,不导入原始宏
kotlin
@OptIn(ExperimentalForeignApi::class)
import math.*

fun calculate() {
    val x = 5
    println(square(x))             // 25
    println(square_double(3.14))   // ~9.8596
    
    println(int_max(10, 20))       // 20
    println(double_max(1.5, 2.7))  // 2.7
    
    println(clamp_int(150, 0, 100)) // 100
}

特殊宏处理

可变参数宏

c
// logging.h
#define LOG_INFO(fmt, ...) printf("[INFO] " fmt "\n", ##__VA_ARGS__)
#define LOG_ERROR(fmt, ...) fprintf(stderr, "[ERROR] " fmt "\n", ##__VA_ARGS__)

包装策略:创建固定参数版本

properties
# logging.def
headers = logging.h
package = logging

---

static inline void log_info_str(const char* msg) {
    LOG_INFO("%s", msg);
}

static inline void log_info_int(const char* fmt, int value) {
    LOG_INFO(fmt, value);
}

static inline void log_error_str(const char* msg) {
    LOG_ERROR("%s", msg);
}
kotlin
@OptIn(ExperimentalForeignApi::class)
import logging.*

fun doLogging() {
    log_info_str("Application started")
    log_info_int("Process ID: %d", 1234)
    log_error_str("Failed to open file")
}

类型转换宏

c
// cast_macros.h
#define TO_INT(x) ((int)(x))
#define TO_UINT(x) ((unsigned int)(x))
#define TO_PTR(x) ((void*)(x))
#define FROM_PTR(ptr, type) ((type)(ptr))
properties
# cast.def
---

static inline int to_int_from_double(double x) {
    return TO_INT(x);
}

static inline unsigned int to_uint_from_int(int x) {
    return TO_UINT(x);
}

static inline void* to_ptr_from_long(long x) {
    return TO_PTR(x);
}

static inline long from_ptr_to_long(void* ptr) {
    return (long)FROM_PTR(ptr, long);
}

字符串化宏

c
// stringify.h
#define STRINGIFY(x) #x
#define CONCAT(a, b) a##b
properties
# stringify.def
---

// 字符串化:在编译时完成
#define VERSION_MAJOR 2
#define VERSION_MINOR 1
static inline const char* get_version_string() {
    return STRINGIFY(VERSION_MAJOR) "." STRINGIFY(VERSION_MINOR);
}

// 拼接:生成新的标识符
#define DEFINE_GETTER(type, name) \
    static type get_##name() { return name; }

DEFINE_GETTER(int, MAX_SIZE)

条件编译宏

特性检测

c
// features.h
#ifdef ENABLE_ADVANCED_FEATURES
void advanced_feature_init();
int advanced_process(int input);
#endif

#ifdef USE_HARDWARE_ACCELERATION
void gpu_init();
#endif

在 def 文件中处理:

properties
# features.def
headers = features.h
package = features

# 启用特性
compilerOpts = -DENABLE_ADVANCED_FEATURES -DUSE_HARDWARE_ACCELERATION

---

// 提供后备实现(当特性未启用时)
#ifndef ENABLE_ADVANCED_FEATURES
static inline void advanced_feature_init() {
    // 空实现或软件后MissionBehavior
}

static inline int advanced_process(int input) {
    return input; // 默认行为
}
#endif

#ifndef USE_HARDWARE_ACCELERATION
static inline void gpu_init() {
    // CPU后备
}
#endif

平台特定宏

properties
# platform.def
headers = platform.h

# 平台特定编译选项
compilerOpts.linux = -DPLATFORM_LINUX -D_GNU_SOURCE
compilerOpts.osx = -DPLATFORM_MACOS -D_DARWIN_C_SOURCE
compilerOpts.mingw = -DPLATFORM_WINDOWS -D_WIN32_WINNT=0x0600

---

// 平台检测包装
#if defined(PLATFORM_LINUX)
static inline const char* platform_name() {
    return "Linux";
}
static inline int platform_id() {
    return 1;
}
#elif defined(PLATFORM_MACOS)
static inline const char* platform_name() {
    return "macOS";
}
static inline int platform_id() {
    return 2;
}
#elif defined(PLATFORM_WINDOWS)
static inline const char* platform_name() {
    return "Windows";
}
static inline int platform_id() {
    return 3;
}
#else
#error "Unsupported platform"
#endif
kotlin
@OptIn(ExperimentalForeignApi::class)
fun detectPlatform() {
    println("Running on: ${platform_name()?.toKString()}")
    println("Platform ID: ${platform_id()}")
}

StableRef 与回调

宏定义的回调

C 库常使用宏定义回调:

c
// callback.h
typedef void (*Callback)(int value, void* userdata);

#define REGISTER_CALLBACK(cb, data) \
    register_callback_impl(cb, data)

void register_callback_impl(Callback cb, void* userdata);
properties
# callback.def
---

static inline void register_callback_wrapper(Callback cb, void* data) {
    REGISTER_CALLBACK(cb, data);
}
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlin.native.ref.*

val callback = staticCFunction<Int, COpaquePointer?, Unit> { value, userdata ->
    if (userdata != null) {
        val handler = userdata.asStableRef<(Int) -> Unit>().get()
        handler(value)
    }
}

fun setupCallback(handler: (Int) -> Unit) {
    val ref = StableRef.create(handler)
    register_callback_wrapper(callback, ref.asCPointer())
}

// 使用
setupCallback { value ->
    println("Received: $value")
}

最佳实践

实践:命名约定

c
// 统一包装函数命名
// 宏名: MACRO_NAME → 包装函数: macro_name_wrap or macro_name

// ✅ 推荐
#define SQUARE(x) ((x) * (x))
static inline int square(int x) { return SQUARE(x); }

// ✅ 也可以
static inline int square_wrap(int x) { return SQUARE(x); }

// ❌ 避免混淆的命名
static inline int sq(int x) { return SQUARE(x); }

实践:类型安全层

kotlin
// Kotlin 安全包装
@OptIn(ExperimentalForeignApi::class)
object MathUtils {
    fun square(x: Int): Int = square_wrap(x)
    fun square(x: Double): Double = square_double_wrap(x)
    
    fun max(a: Int, b: Int): Int = int_max_wrap(a, b)
    fun max(a: Double, b: Double): Double = double_max_wrap(a, b)
    
    fun clamp(value: Int, range: IntRange): Int =
        clamp_int_wrap(value, range.first, range.last)
}

// 使用
val result = MathUtils.square(5)
val maximum = MathUtils.max(10.5, 20.3)
val clamped = MathUtils.clamp(150, 0..100)

实践:文档化宏包装

c
/**
 * Wrapper for SQUARE(x) macro
 * Original definition: #define SQUARE(x) ((x) * (x))
 * 
 * @param x Value to square
 * @return x squared
 */
static inline int square(int x) {
    return SQUARE(x);
}

C 宏无法直接映射到 Kotlin,需要通过 static inline 函数包装。合理组织包装代码能提供类型安全、易用的 Kotlin API。