动态创建C++完美转发的函数参数

如何解决动态创建C++完美转发的函数参数

我遇到了一个问题,目前我还没有找到好的解决方案。

本质上,我需要将参数传递给 C++ 可变参数模板化函数。该函数具有以下通用签名/形式:

template <typename... Options>
ReaderType GetAList(std::string const& name,Options&&... options)
{ /*... do stuff ...*/ }

我已经阅读了几个关于完美转发、可变参数模板、动态参数列表等的 StackOverflow 问答。特别是这些:

C++ generic function call with varargs parameter

Dynamically creating a C++ function argument list at runtime

Create function call dynamically in C++

C++ forwarding reference and r-value reference

这些只是我读过的关于这个主题的少数 StackOverflow 线程(如果你愿意的话)。我还在 LinkedIn 网站上发现了一篇关于可变参数模板和函数的有趣文章:

Modern C++ and Variadic Functions: How to Shoot Yourself in the Foot and How to Avoid It

说这个的重点是我已经尝试做功课了,但是还没有结果。于是我决定问一个问题。

这里的问题是这些选项直到运行时才知道。因此,我必须动态地向 GetAList 方法调用添加参数。在上面的例子中,参数在编译时是已知的,例如:

interceptor(gWrappers[1],10.12,12.10); // <--- example 1: params are given statically at compile time
func_map["printall"]("iic",5,10,'x');    // <--- example 2: same approach

这在我的场景中不起作用,因为我不知道用户将从可用选项中选择哪些选项。所以我必须看看他们要使用哪些选项,并动态形成一个参数列表。

在动态参数生成的其他示例中(这是很好的示例,我的意思是,我确实从阅读这些线程中学到了一些东西),关键点以参数类型为中心。也就是说,使用了一种中间方法,在该方法中您传递要作为第一个参数调用的函数,然后是参数的列表或向量。这是其中的一个简短片段:

template <typename Ret,typename...Arg>
Ret call (Ret (*func)(Arg...),std::list<boost::any> args)
{ /*...do stuff... */ }

int foo(int x,double y,const std::string& z,std::string& w)
{ /*...more stuff...*/ }

int main ()
{
    std::list<boost::any> args;
    args.push_back(1);
    args.push_back(4.56);
    const std::string yyy("abc");
    std::string zzz("123");
    args.push_back(std::cref(yyy));
    args.push_back(std::ref(zzz));
    call(foo,args);
}

最终使用这种方法,参数参数类型是通过非模板化函数定义(即上面示例中的函数 foo)推导出来的。很酷。

但是,我不能这样做,因为 GetAList 方法的第二个参数是一个包含前向引用的参数包,它们是 r 值。我确实查看了这些方法的源代码(例如 GetAList),并将选项值进一步转发到它们的实现中(通过 std::forward)。

我确实考虑过使用中间方法,这些方法具有与给定方法的可用选项的所有可能排列匹配的参数列表,例如 GetAList。然后这些中间方法将调用相应的方法,例如 GetAList。这将适用于上述模型。

然而,随着给定方法的可用选项数量的增加,匹配方法的数量会急剧增加。也就是说,您最终可能会使用 25、100 或更多方法来涵盖所有选项排列。这几乎和试图用宏魔法解决这个问题一样丑陋。

因此,我试图避免整个“如果这些参数调用此方法,如果这些参数调用另一个,等等”类型的解决方案。相反,我一直希望找到某种类型的 C++ 语法魔杖,我可以挥动它来解决问题。到目前为止无济于事,因此发布了我的问题。

为了完成,对 GetAList 方法(及其兄弟)的典型调用如下所示:

ReaderType myReader = client->GetAList( someName,optionsNamespace::PostName(std::string("whatever") ),optionsNamespace::MaxInList(1) );

这是我直到运行时才知道的最后两个参数。因此我必须在方法调用期间动态创建它们。

最后,可能值得注意的是(不是真的,但为了完成)这里涉及的调用堆栈包含一个 C-to-C++ 桥,其中有关最终用户选择的选项的信息来到我以 C varargs 列表形式显示的堆栈部分。但是,该列表内容可能需要转码,因此我无法按原样使用该内容。因此,我预处理 varargs 内容,必要时进行转码,并将结果保存在“选项块”中,因为缺少更好的术语。然后在我也负责的堆栈的 C++ 部分中处理该选项块;这就是这种魔法需要发生的地方。我需要筛选选项块,确定选择了哪些选项,检索它们的值并使用这些值在适当的方法调用语句(例如 GetAList)中创建选项对象。

读完后,我希望它是有道理的。

无论如何,我仍在继续试验,试图找到答案。但是,如果你们中有任何人有意见/反馈,我会很有兴趣阅读。

编辑

一些回复要求澄清我的问题。对任何不清楚的地方表示歉意。我的问题是:如何在运行时动态创建可以发送到具有可变参数模板的 C++ 函数的参数列表?

正如我所提到的,关于 GetAList 方法的典型用例场景是静态指定您想要的选项对象。这意味着您的代码使用“硬编码”的 arg 列表调用该方法调用的方法。像这样:

ReaderType myReader = client->GetAList( someName,// <--- This is option object #1
    optionsNamespace::MaxInList(1) );                     // <--- This is option object #2

我需要能够调用上面显示的 GetAList 方法,但是选项参数不能直接“硬编码”到上面显示的调用中(选项对象 #1 和 #2)。我必须在运行时根据最终用户选择的选项动态创建选项对象的参数列表。

最初我探索了矢量方法。所以我们的想法是筛选选项列表,创建选项对象并同时将其推送到向量的末尾。然后调用一个中间函数,将向量扩展为逗号分隔的参数列表,可以传递给 GetAList。然而,这也存在问题。首要的是确定参数类型。正如我最初的解释中所述,我阅读的示例中的类型是通过检查要调用的结束函数(上面示例中的 foo)推导出来的。但是,GetAList 没有可以从中推断出参数类型的参数列表,就像 foo 一样。所以我研究了如何使用 std::forward 将对象从中间函数转发到 GetAList,但我也没有想出任何工作。

这是否提供了清晰度?如果没有,我可以再试一次。感谢您到目前为止的回复,我很感激!

解决方法

绝对不可能

一个简短的问题:参数的数量在运行时是已知的,但函数调用应该在编译时知道它。怎么解决?

通常的解决方案是尝试构建所有可能的调用,然后选择一个(类似于您最初提到的 std::visit)。但如果使用 boost::anystd::any 但您不知道它可能具有的所有类型,则它很糟糕,有时无法达到目的。

也许你会想到可变函数虚拟函数。但它们都不能跨越运行时和编译时之间的差距可变参数,只需从参数或其他东西中获取一些附加信息,然后确定要做什么。如果某些东西依赖于 type,它仍然应该依赖一个大的 switch 语句,然后选择一个分支。

如果你真的想这样做,也许你必须做出一些妥协:

  1. 首先,您必须知道所有可能的参数类型,并为每个参数指定一个唯一 ID。
  2. 其次,构建从所有 ID 组合到所有可能函数调用的映射(记住使无效的函数调用可编译)。您可以使用 switch 语句(有时不够用)、虚函数(将 ID 映射到从基类派生的对象)或大映射表。
  3. 第三,也许您应该创建一些辅助类(例如 std::ref)来保持值类别和引用的不变性。
  4. 最后,当您获得参数时,将它们适当地存储并获得 ID 组合,然后进行实际的函数调用。在这种情况下,您将获得不确定类型的返回值。如果它有用并且实际上是多态的,则还必须构建返回类型映射表,然后在实际使用位置使用大 switch 语句。此过程类似于使用函数类型 void(std::any) 再次执行 1-2-3。

事实上,这类似于 std::variantstd::visit 所做的。因此您还可以创建 std::variant< *all possible types* > 的包装器,然后使用 std::visit

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res