【问题标题】:_Generic function with several parameters_具有多个参数的通用函数
【发布时间】:2021-08-05 18:04:33
【问题描述】:

我正在使用几个类似的函数,并希望使其中一个可重载。
基本函数需要3个参数,其连续展开需要4个或更多。

#define register_read_write(action, parameter, reg_value, ...) 
        _Generic(&(uint32_t[]){__VA_ARGS__}, 
        uint32_t(*)[2]: register_read_write_with_limits,
        uint32_t(*)[1]: register_read_write_with_upper_limit)
            ((action), (parameter), (reg_value), (__VA_ARGS__))

声明

void register_read_write_with_limits(access_t action, parameter_t parameter, uint16_t *reg_value, 
                                     uint32_t value_min, uint32_t value_max);

void register_read_write_with_upper_limit(access_t action, parameter_t parameter, uint16_t *reg_value, 
                                     uint32_t value_max);

它工作正常,但我无法添加带有 3 个参数的基本函数:

void register_read_write(access_t action, parameter_t parameter, uint16_t *reg_value)

我试试:

#define register_read_write(action, parameter, reg_value, ...) 
        _Generic(&(uint32_t[]){__VA_ARGS__}, 
        uint32_t(*)[2]: register_read_write_with_limits,
        uint32_t(*)[1]: register_read_write_with_upper_limit,
        uint32_t(*)[0]: register_read_write)
            ((action), (parameter), (reg_value), (__VA_ARGS__))

但是:

错误:'uint32_t ()[0]' {aka 'long unsigned int ()[0]'} 类型的 '_Generic' 选择器与任何关联都不兼容 #define register_read_write(action, parameter, reg_value, ...) _Generic(&(uint32_t[]){VA_ARGS},

紧随其后....

我正在开发我的通用函数,但遇到了另一个问题。

#define FIRST_ARG(value, ...) (value)

#define generic_uint8_read_write(action, parameter, ...) 
        _Generic(&(uint32_t[]){(uintptr_t)__VA_ARGS__},
        uint32_t(*)[1]: register_uint8_read_write)(action, parameter, __VA_ARGS__)

#define generic_uint16_read_write(action, parameter, ...) _Generic(&(uint32_t[]){(uintptr_t)__VA_ARGS__},                
         uint32_t(*)[2]: register_read_write_with_limits,
        uint32_t(*)[1]: register_read_write_with_upper_limit,
        uint32_t(*)[0]: register_read_write)(action, parameter, __VA_ARGS__)

#define generic_read_write(action, parameter, ...)   
        _Generic(FIRST_ARG(__VA_ARGS__),
        uint8_t*: generic_uint8_read_write(action, parameter, __VA_ARGS__),
        uint16_t* : generic_uint16_read_write(action, parameter, __VA_ARGS__))

我不知道为什么,但它没有正确检测指针类型。

我变了

#define generic_read_write(action, parameter, value, ...)   
        _Generic((value),
        uint8_t*: generic_uint8_read_write(action, parameter, value, __VA_ARGS__),
        uint16_t* : generic_uint16_read_write(action, parameter, value, __VA_ARGS__))

还是失败了:-(

请给我一些建议。

问候
附言我正在寻找关于“_Generic”功能如何工作的好教程。

【问题讨论】:

  • 顺便说一句,这是对参数计数执行通用选择的一个非常聪明的技巧。
  • 有理由使用Generic吗?你似乎根本不关心参数的类型,你只是将它们转换为uint32_t[]。只需在参数数量上重载宏即可。
  • but it doesn't detect pointer type correctly 宏被预处理器扩展,_Generic 被编译器解析。所以modbus_generic_uint8_read_write(stuf) 总是扩展,因为预处理器扩展了它。我仍然没有完全理解使用&(uint32_t[]) 的意义。
  • @KamilCuk 我找到了一个例子并采用了它。我还不太明白_Generec的语法

标签: c variadic-macros


【解决方案1】:

您的尝试至少有两个问题:

  1. C 不支持长度为 0 的数组。某些实现接受它们作为扩展,就像您的那样,但作为扩展,可能带有警告,例如不在您使用的上下文中工作。但这没有实际意义,因为

  2. 如果选择了uint32_t(*)[0] 替代项,那么您最终会得到register_read_write(a, p, r,) 形式的语法无效函数调用(注意结尾的逗号)。

由于您的宏具有非可变参数,您可以通过将其中一个吸收到可变参数中来解释这一点:

void register_read_write(action_t action, parameter_t parameter, uint16_t *reg_value);

void register_read_write_with_limits(action_t action, parameter_t parameter, uint16_t *reg_value, 
                                     uint32_t value_min, uint32_t value_max);

void register_read_write_with_upper_limit(action_t action, parameter_t parameter, uint16_t *reg_value, 
                                     uint32_t value_max);

#define register_read_write(a, p, ...)  \
        _Generic(&(uint32_t[]){(uintptr_t) __VA_ARGS__},  \
        uint32_t(*)[3]: register_read_write_with_limits, \
        uint32_t(*)[2]: register_read_write_with_upper_limit, \
        uint32_t(*)[1]: register_read_write) \
            ((a), (p), __VA_ARGS__)

void bar() {
    int a = 0;
    int b = 0;
    uint16_t u16 =  0;
    uint32_t min = 0;
    uint32_t max = 0;

    register_read_write(a, b, &u16, min, max);
    register_read_write(a, b, &u16, max);
    register_read_write(a, b, &u16);
}

现在,您在每个通用替代方案中都有一个正长度数组类型,并且它工作正常。请注意,这里需要(uintptr_t) 强制转换,因为第三个宏参数的预期类型与uint32_t 不兼容,并且一些编译器会警告将 64 位指针强制转换为 32 位整数。请注意,强制转换只会影响第一个可变参数,并且无论如何,它和将结果隐式转换为类型 uint32_t 都不会被实际计算 - 只需要表达式的类型。

【讨论】:

  • 效果很好。我几乎开始理解这个想法了。如果我要添加: void register_uint8_read_write (action_t action, parameter_t parameter, uint8_t * reg_value) 我需要使用嵌套泛型吗?
  • 是的,我认为你需要一个嵌套的泛型,@PawełKurzawa。
【解决方案2】:

考虑这样的实现:

#include <stdint.h>
#include <stdio.h>
#define BODY  { printf("%s\n", __func__); }
void func_u8_0(int action, int param, uint8_t *reg) BODY
void func_u8_1(int action, int param, uint8_t *reg, int min) BODY
void func_u8_2(int action, int param, uint8_t *reg, int min, int max) BODY
void func_u16_0(int action, int param, uint16_t *reg) BODY
void func_u16_1(int action, int param, uint16_t *reg, int min) BODY
void func_u16_2(int action, int param, uint16_t *reg, int min, int max) BODY

#define register_read_write_1(a, p, r) \
    _Generic((r), \
        uint8_t*: func_u8_0, \
        uint16_t*: func_u16_0 \
    )
#define register_read_write_2(a, p, r, min) \
    _Generic((r), \
        uint8_t*: func_u8_1, \
        uint16_t*: func_u16_1 \
    )
#define register_read_write_3(a, p, r, min, max) \
    _Generic((r), \
        uint8_t*: func_u8_2, \
        uint16_t*: func_u16_2 \
    )
#define register_read_write_N(_3,_2,_1,N,...) register_read_write_##N
#define register_read_write(a, p, ...) \
    register_read_write_N(__VA_ARGS__,3,2,1)(a, p, __VA_ARGS__)(a, p, __VA_ARGS__)

int main() {
    register_read_write(1, 1, (uint8_t*)0);
    register_read_write(1, 1, (uint8_t*)0, 1);
    register_read_write(1, 1, (uint8_t*)0, 1, 1);
    register_read_write(1, 1, (uint16_t*)0);
    register_read_write(1, 1, (uint16_t*)0, 1);
    register_read_write(1, 1, (uint16_t*)0, 1, 1);
}

即用宏检测参数数量,用_Generic检测类型。一种工具,一种工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-25
    • 2020-08-29
    • 1970-01-01
    • 2011-10-13
    • 1970-01-01
    • 2013-08-02
    相关资源
    最近更新 更多