首页 > 解决方案 > 将函数指针分配给 constexpr 结构中的 typedef 函数的正确 C++ 方法是什么?

问题描述

如果问题措辞不完全准确,请原谅我,但我正在尝试做的(我在使用 MSVC /std:c++latest 编译时所做的)是为一个非常基本的 shell 类创建一个配置项的 constexpr 结构. 其中一些项目是依赖于应用程序/平台的 io 函数,可读写。以下代码适用于我在添加到现有项目之前修改的 VS 解决方案。

...来自仅标题类

typedef size_t (*read_func_t)(void* const buf, size_t nbytes);
typedef size_t (*write_func_t)(const void* buf, size_t nbytes);

typedef struct shell_cfg_t {
  size_t buffer_size;
  read_func_t read;
  write_func_t write;
  size_t max_args;
} shell_cfg_t;

template<shell_cfg_t cfg>
class Shell
{
  public:

    Shell(const cmd_t * const cmd_table)
    : m_buf{}
    , m_args{}
    , m_read{cfg.read}
    , m_write{cfg.write}
    {
      ms_cmd_table = cmd_table;
    }
};

...来自 main.cpp

static size_t read_impl(void* const buf, size_t nbytes)
{
  // some code
  return bytes_read;
}

static size_t write_impl(const void* buf, size_t nbytes)
{
  // some code
  return bytes_written;
}

constexpr shell_cfg_t cfg =
{
  .buffer_size = 64,
  .read = read_impl,
  .write = write_impl,
  .max_args = 8
};

int main()
{
  Shell<cfg> sh(shell_cmd_table);

  std::string user_input_str;
  std::getline(std::cin, user_input_str);
  user_input_str += sh.kEoL;

  byte_recvd_handler((uint8_t*)(user_input_str.data()), user_input_str.length());

  sh.process();

  return 0;
}

这里的问题代码似乎是将 read_impl 和 write_impl 分配给结构的 .read 和 .write 。来自 GNU 10.2.0 的错误如下(底部的完整错误):

"error: 'read_impl' is not a valid template argument of type 'size_t (*)(void*, size_t)' {aka 'long long unsigned int (*)(void*, long long unsigned int)'} because 'read_impl' is not a variable, .read = read_impl,"

我正在处理的 GCC 和 MSVC 之间的 C++20 实现是否存在差异,或者此代码是否符合标准?constexpr 结构和命名初始化列表是我认为的新事物,但我认为以这种方式分配函数指针与新的 std 标准没有任何关系。另一种方法是对不是 nullptr 的读/写函数进行运行时检查,这是将配置结构与模板一起使用的重点。有没有更好的方法可以与 C++17 兼容?

编辑:...与此文件相关的完整错误。整个项目输出将是 1000+ 行

../Examples/Example_Shell.cpp: In function 'int main()':
../Examples/Example_Shell.cpp:47:11: error: 'read_impl' is not a valid template argument of type 'size_t (*)(void*, size_t)' {aka 'long long unsigned int (*)(void*, long long unsigned int)'} because 'read_impl' is not a variable
   47 |   .read = read_impl,
      |           ^~~~~~~~~
../Examples/Example_Shell.cpp:71:28: error: invalid conversion from 'const cmd_t*' {aka 'const Kernel::Shell::cmd_t*'} to 'int' [-fpermissive]
   71 |   Kernel::Shell::Term<cfg> sh(shell_cmd_table);
      |                            ^~~~~~~~~~~~~~~
      |                            |
      |                            const cmd_t* {aka const Kernel::Shell::cmd_t*}
../Examples/Example_Shell.cpp:75:24: error: request for member 'kEoL' in 'sh', which is of non-class type 'int'
   75 |   user_input_str += sh.kEoL; // for example only. getline does not capture end line
      |                        ^~~~
../Examples/Example_Shell.cpp:79:6: error: request for member 'process' in 'sh', which is of non-class type 'int'
   79 |   sh.process();
      |      ^~~~~~~

标签: c++functionpointersstructconstexpr

解决方案


而是定义这个constexpr定义一个类,然后模板将其用作类型参数:

struct shell_default_cfg_t
{
    size_t buffer_size;
    read_func_t read;
    /* static */ size_t read(void* const buf, size_t nbytes) const {
       return read_impl(buf, nbytes);
    }

    /* static */ size_t write_impl(const void* buf, size_t nbytes) {
       return write_impl(buf, nbytes);
    }
};

template<typename Cfg>
class Shell
{
  public:

    Shell(const cmd_t * const cmd_table, Cfg cfg)
    : m_buf{}
    , m_args{}
    , m_cfg{std::move(cfg)}
    {
      ms_cmd_table = cmd_table;
    }

    size_t read(void* const buf, size_t nbytes) const {
       return cfg.read(buf, nbytes);
    }

    Cfg m_cfg;
};

推荐阅读