首页 > 解决方案 > 参数化的通用 C 宏

问题描述

我正在尝试在 C 中构建一个伪泛型类型。本质上,我正在尝试Option<T>使用一个预定义的、受限制的类型列表来克隆 Rust,该列表允许为T.

显然,C 并不真正适合这个——我这样做主要是为了看看我能走多远(而不是我期望在实际生产代码中使用的东西)。为此,任何丑陋的黑客都是公平的游戏。

到目前为止,我为所有提供的类型构建了一组单独的特定于内部类型的函数。它看起来像这样:

标题:

#pragma once

#define ALL_OPTIONS \
    OPTION_INSTANCE(option_bool, bool) \
    OPTION_INSTANCE(option_double, double) \
    OPTION_INSTANCE(option_int, int)

#define OPTION_INSTANCE(name, inner)                                          \
    typedef struct {                                                          \
        bool is_some;                                                         \
        inner val;                                                            \
    } name##_t;

ALL_OPTIONS

#undef OPTION_INSTANCE

#define OPTION_INSTANCE(name, inner)                                          \
    name##_t name##_some(inner val);                                          \
    name##_t name##_none(void);                                               \
    bool name##_is_some(name##_t self);                                       \
    bool name##_is_none(name##_t self);                                       \

ALL_OPTIONS

#undef OPTION_INSTANCE

执行:

#include "option.h"

#define OPTION_INSTANCE(name, inner) \
    name##_t name##_some(inner val) {                                         \
        return (name##_t) {                                                   \
            .is_some = true,                                                  \
            .val = val,                                                       \
        };                                                                    \
    }                                                                         \
                                                                              \
    name##_t name##_none(void) {                                              \
        return (name##_t) {                                                   \
            .is_some = false,                                                 \
        };                                                                    \
    }                                                                         \
                                                                              \
    bool name##_is_some(name##_t self) {                                      \
        return self.is_some;                                                  \
    }                                                                         \
                                                                              \
    bool name##_is_none(name##_t self) {                                      \
        return !self.is_some;                                                 \
    }

ALL_OPTIONS

#undef OPTION_INSTANCE

请注意,在我的实际代码中,我为生成的类型定义了更多函数。

这工作得很好,尽管我所做的主要是减少实现样板。下一步将是实施option_is_some(无类型限定),它可以接受任何 option_<inner>_t

利用 C11 泛型,我可以通过手动宏来做到这一点:

#define option_is_some(self)                                                  \
    _Generic((self),                                                          \
        option_bool_t: option_bool_is_some,                                   \
        option_double_t: option_double_is_some,                               \
        option_int_t: option_int_is_some,                                     \
    )(self)

但这必然会重复 中定义的类型列表ALL_OPTIONS。我真正想做的是

#define OPTION_INSTANCE(name, inner) \
    name##_t: name##_is_some,

#define option_is_some(self)                                                  \
    _Generic((self),                                                          \
        ALL_OPTIONS                                                           \
        default: false                                                        \
    )(self)

#undef OPTION_INSTANCE

但这失败了,因为在使用ALL_OPTIONS时会扩展option_is_some(其中OPTION_INSTANCE将未定义)。

所以,我正在寻找替代品。我很乐意采用一种完全不同的方法来定义通用类型列表(而不是ALL_OPTIONShack)——但是,我确实希望保留添加新支持的内部类型只需要在单个位置进行更改的属性。

标签: cgenericsc-preprocessor

解决方案


只需访问宏本身中的成员:

 #define option_is_some(self)  ((self).is_some)

总的来说,你的实现很奇怪。没有一个中心ALL_OPTIONS位置 - 一次只做一个选项,彼此分开。文件在 C 中分为头文件和源文件。

#define OPTION_HEADER(name, inner) \
    typedef struct {                                                          \
        bool is_some;                                                         \
        inner val;                                                            \
    } name##_t; \
\
    name##_t name##_some(inner val);                                          \
    name##_t name##_none(void);                                               \
    bool name##_is_some(name##_t self);                                       \
    bool name##_is_none(name##_t self);

#define OPTION_SOURCE(name, inner) \
    name##_t name##_some(inner val) {                                         \
        return (name##_t) {                                                   \
            .is_some = true,                                                  \
            .val = val,                                                       \
        };                                                                    \
    }     \
    etc...

#define OPTION_HEADER_AND_SOURCE(name, ...) \
       OPTION_HEADER(name, __VA_ARGS__)
       OPTION_SOURCE(name, __VA_ARGS__)

然后,您只需执行以下选项:

OPTION_HEADER_AND_SOURCE(option_bool, bool)
OPTION_HEADER_AND_SOURCE(option_double, double)
OPTION_HEADER_AND_SOURCE(option_int, int)

您可以查看我发现的其他项目:https ://github.com/tylov/STC和https://github.com/glouw/ctl,它们使用宏模板在 C 中实现各种已知容器来自 C++。


推荐阅读