简体   繁体   English

C - 是否可以使用可变参数宏实现缩减操作?

[英]C - Is it possible to implement a reduction operation using variadic macros?

This question is nearly a duplicate, but I figured I'd ask again since it's pretty old and the language may have evolved: Variadic recursive preprocessor macros - is it possible?这个问题几乎是重复的,但我想我会再问一次,因为它已经很老了,而且语言可能已经发展: Variadic recursive preprocessor macros - is it possible?

I would like to implement a constant ONE_HOT encoding operation and I'm wondering if this is possible using variadic macros.我想实现一个常量 ONE_HOT 编码操作,我想知道这是否可以使用可变参数宏。

I would like to compute a constant one-hot encoding of my enum elements and store it in an uint32 .我想计算我的enum元素的常量单热编码并将其存储在uint32中。 Basically, reduce all arguments of ONE_HOT(a, b, c, ...) as (1 << a) | (1 << b) | (1 << c)...基本上,将ONE_HOT(a, b, c, ...)所有 arguments 减少为(1 << a) | (1 << b) | (1 << c)... (1 << a) | (1 << b) | (1 << c)... (1 << a) | (1 << b) | (1 << c)... . (1 << a) | (1 << b) | (1 << c)...

Option A: Bit-shift each entry when computing.选项 A:计算时对每个条目进行位移。

typedef enum {
  FIRST,
  SECOND,
  THIRD,
  // More entries
  MY_ENUM_NUM_ENTRIES // Guaranteed to never be greater than sizeof(uint32_t)*8 by design.
} MY_ENUM_TYPE;

// In this case, only FIRST and THIRD are "set".
uint32_t expected_entries = (1<<FIRST) | (1<<THIRD);

// Check if set
if(expected_entries & (1<<SECOND)){
  // Do something.
}

Option B: Modify the enum declaration and assign it power-of-2 values.选项 B:修改enum声明并为其分配 2 的幂值。

typedef enum {
  FIRST = (1 << 0),
  SECOND = (1 << 1),
  THIRD = (1 << 2),
  // More entries
  MY_ENUM_NUM_ENTRIES // Downside: This is now broken. Requires a workaround.
} MY_ENUM_TYPE;

// Upside: The one-hot operation is nicer to read and write.
uint32_t expected_entries = FIRST | THIRD;

// Check if set
if(expected_entries & SECOND){
  // Do something.
}

Option C: Variadic Macro?选项 C:可变参数宏?

Is there a way to define my operation such that the one-hot computation is reduced and evaluated at compile-time whenever possible?有没有一种方法可以定义我的操作,以便尽可能在编译时减少和评估单热计算?

  1. I'm aware that recursive macro definitions are discouraged since the pre-processor makes a single-pass.我知道不鼓励递归宏定义,因为预处理器进行单次传递。
  2. Is it possible to avoid defining 32 macros, one for each arg count?是否可以避免定义 32 个宏,每个 arg 计数一个? See the linked question at the start.请参阅开头的链接问题。
#define IS_HOT(encoded, bit) (encoded & (1<<bit))
#define SET_HOT(encoded, bit) (encoded | (1<<bit))
#define CLEAR_HOT(encoded, bit) (encoded & (0xFFFFu^(1<<bit)))

// PSEUDO-CODE.
// I want all values (a, b, c, ...) passed into ONE_HOT to be shifted
// and bitwise OR'd together to form the one-hot encoding.
#define ONE_HOT(a, b, c, ...) (1 << a) | (1 << b) | (1 << c) | ONE_HOT(##__VA_ARGS__)

// END PSEUDO-CODE

typedef enum {
  FIRST,
  SECOND,
  THIRD,
  // More entries
  MY_ENUM_NUM_ENTRIES // Guaranteed to never be greater than sizeof(uint32_t)*8 by design.
  // BONUS: we don't need to redefine our original enum.
} MY_ENUM_TYPE;

// Upside: The one-hot operation is nicer to read and write.
uint32_t expected_entries = ONE_HOT(FIRST, THIRD);
// This is terrible
//   uint32_t expected_entries = SET_HOT(SET_HOT(encoded, FIRST), THIRD);
// Check if set
if(IS_HOT(expected_entries, SECOND)){
  // Do something.
}

onehot.h

Implemented with the "Map-Macro" pattern, as suggested.按照建议使用“Map-Macro”模式实施。

#ifndef ONEHOT_H
#define ONEHOT_H

// Defining the map-macro operation per
// https://github.com/swansontec/map-macro
#define MAP_OUT

#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0 (EVAL0 (__VA_ARGS__))
#define EVAL2(...) EVAL1 (EVAL1 (__VA_ARGS__))
#define EVAL3(...) EVAL2 (EVAL2 (__VA_ARGS__))
#define EVAL4(...) EVAL3 (EVAL3 (__VA_ARGS__))
#define EVAL(...)  EVAL4 (EVAL4 (__VA_ARGS__))

#define MAP_END(...)

#define MAP_GET_END() 0, MAP_END
#define MAP_NEXT0(test, next, ...) next MAP_OUT
#define MAP_NEXT1(test, next) MAP_NEXT0 (test, next, 0)
#define MAP_NEXT(test, next)  MAP_NEXT1 (MAP_GET_END test, next)

#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)

// Final structure of the map-macro
#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))

// One-Hot Definition
#define ONE_HOT_SHIFT(a) (1u << a) | 
#define ONE_HOT(...) MAP(ONE_HOT_SHIFT, __VA_ARGS__) 0

#define IS_HOT(encoded, bit)    (encoded & (1 << bit))
#define SET_HOT(encoded, bit)   (encoded | (1 << bit))
#define CLEAR_HOT(encoded, bit) (encoded & (0xFFFFu^(1 << bit)))
#define INTERSECT_HOT(a, b)     (a & b)
#define UNION_HOT(a, b)         (a | b)
#define EQUALS_HOT(a, b)        (~(a^b))
#define A_CONTAINS_ALL_B(a, b)  (EQUALS_HOT(INTERSECT_HOT(a, b), b))

#endif

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM