宏与预处理器
源: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 1kotlin
@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);
}
#endifproperties
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##bproperties
# 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"
#endifkotlin
@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。