首页 > 解决方案 > 如何从 C 宏调用 C++ 重载函数

问题描述

我正在添加一个在 C 文件中使用的宏,它应该采用可变数量的参数,应该根据类型和参数数量进行不同的处理。如果它是纯 C++,它很容易通过重载函数来实现,但是我如何将 C 宏中的可变参数传递到 C++ 与 C 和 C++ 的混合?

C 文件被限制为使用 gcc 编译。在宏定义中,我将可变参数传递给 C 包装函数。由于参数的数量未知,我有一个宏来计算参数并将其传递给 va_list。但是使用这种方法,我不知道要灵活地将任何参数传递给 C++ 函数的参数类型。我包含了反映当前代码结构的相关代码片段,跳过了 cpp 文件中的实际处理逻辑和其他不相关的信息。

在 use.c 中:

#include "macro.h"
LOG(id, lvl, params);

在宏.h 中:

#define LOG(_MSG_ID_, _LOG_LVL_, ...) \
  log_data(&hdr, ##__VA_ARGS__); \

在 logger.h 中:

#define GET_NARG(_1, _2, _3, _4, N, ...) N
#define COUNT_VARARGS(...) GET_NARG(__VA_ARGS__, 4, 3, 2, 1)
#define log_data(p_hdr, ...) \
  log_data_c(p_hdr, COUNT_VARARGS(__VA_ARGS__), ##__VA_ARGS__);

#ifdef __cplusplus
class LOGGER
{
 public:
  void log_data(LOG_HEADER_s* hdr);
  void log_data(LOG_HEADER_s* hdr, uint16_t val);
  void log_data(LOG_HEADER_s* hdr, uint64_t val);
  void log_data(LOG_HEADER_s* hdr, uint32_t val1, uint32_t val1);
  // and other overloaded functions
  static inline LOGGER& getInstance() { return m_instance; }
 private:
  static LOGGER m_instance;
};
#else
typedef struct LOGGER LOGGER;
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern void log_data_c(LOG_HEADER_s* hdr, int n, ...);
#ifdef __cplusplus
}
#endif

在 logger.cpp 中:

#include "logger.h"
#include <stdarg.h>

LOGGER LOGGER::m_instance = LOGGER();
#define LOGGER_Instance LOGGER::getInstance()

#ifdef __cplusplus
extern "C" {
#endif
void log_cmn_data(LOG_HEADER_s* hdr, int n, ...)
{
  va_list args;
  va_start(args, n);
  LOGGER_Instance.log_data(va_arg(args, LOG_HEADER_s*));
  va_end(args);
}
#ifdef __cplusplus
}
#endif

理想的情况是将宏中的可变参数传递给 C++ 重载函数的调用。欢迎任何实现结果的解决方法。我一直试图让它工作一段时间,但我还没有找到处理相同场景的帖子。任何帮助表示赞赏。

标签: c++coverloadingvariadic-functions

解决方案


由于参数的数量未知,我有一个宏来计算参数并将其传递给 va_list。但是使用这种方法,我不知道要灵活地将任何参数传递给 C++ 函数的参数类型。

没错,你不知道。可变参数函数的 C 机制不直接向被调用函数提供有关可变参数的数量或类型的任何信息。被调用的函数必须使用从其参数中收集的假设和信息的组合来做出决定。该printf函数是典型的示例:它通过分析提供的格式字符串来确定变量参数的数量及其类型(如果实际提供的参数与格式不匹配,则会造成严重破坏)。

假设您对支持的变量参数的数量设置了一个固定的、人为的上限,您确实可以通过可变参数宏来计算它们,如图所示。然而,预处理器可以使用的类型信息很少,并且没有适用的机制可以将这些信息很少用于您的目的。

函数重载的常用 C 替代方法是简单地编写具有不同名称的函数。如果您有类似的函数,主要区别在于参数编号和类型,那么您可以为它们提供传达这些类型的相关名称。例如,

void log_data(LOG_HEADER_s *);
void log_data_u16(LOG_HEADER_s* hdr, uint16_t);
void log_data_u64(LOG_HEADER_s* hdr, uint64_t);
void log_data_u32_u32(LOG_HEADER_s* hdr, uint32_t, uint32_t);

或者,给他们起名字来传达他们特定签名的目的可能更合适。我倾向于怀疑,与尝试通过单个可变参数接口复用几个不同的日志记录函数相比,按照这些思路的方法对您来说效果更好。

另一方面,如果您坚持提供单个可变参数函数接口,那么您可以在 C 端在其前面放置多个用于目的的宏,每个宏对应一个支持的后端签名。这些不需要是可变的(除非相应的特定函数是)。这对您有利,因为您至少会从编译器获得参数计数验证(对于通过宏的调用),并且它们可以提供所需的任何额外参数来将预期的参数数量和类型传递给可变参数接口功能。


推荐阅读