[英]How can we call a function with “parameter=value” in C++?
When reading codes, we will find some functions like this. 阅读代码时,我们会找到一些这样的函数。
g_spawn_async(NULL, new_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
I think nobody can figure out what is the meaning of every parameter. 我想没有人能弄清楚每个参数的含义是什么。 In order to understand the code, we have to find the declaration of the function.
为了理解代码,我们必须找到函数的声明。
gboolean g_spawn_async (const gchar *working_directory,
gchar **argv,
gchar **envp,
GSpawnFlags flags,
GSpawnChildSetupFunc child_setup,
gpointer user_data,
GPid *child_pid,
GError **error);
How can we call a function like the following format in C++? 我们如何在C ++中调用类似以下格式的函数?
g_spawn_async(working_directory=NULL,
argv=new_argv,
envp=NULL,
flags=G_SPAWN_SEARCH_PATH,
child_setup=NULL,
user_data=NULL,
child_pid=NULL,
error=NULL);
I think this one will be more readable and I can understand the code without looking for the declaration of the function. 我认为这个将更具可读性,我可以理解代码而无需查找函数的声明。
I know Python can do this. 我知道Python可以做到这一点。 How can C++ do this?
C ++如何做到这一点?
C++ doesn't support this natively, so you can't do it with just any old existing function. C ++本身不支持这种功能,因此您不能只使用任何旧的现有功能。 If you're creating your own API though, you can use what's called the Named Parameter Idiom to emulate it.
如果你正在创建自己的API,你可以使用所谓的命名参数成语来模拟它。 The example from the link:
链接中的示例:
File f = OpenFile("foo.txt")
.readonly()
.createIfNotExist()
.appendWhenWriting()
.blockSize(1024)
.unbuffered()
.exclusiveAccess();
This is not possible in C or C++. 这在C或C ++中是不可能的。
I understand your pains with this. 我理解你的痛苦。 I personally think that it is a sign of bad design to have a function take over 9000 arguments, especially if most of them are
NULL
or placeholder values. 我个人认为,有一个函数占用9000个参数是一个糟糕设计的标志,特别是如果它们中的大多数是
NULL
或占位符值。 Many POSIX-standardized functions for example take some kind of struct
that accumulates all necessary values into one, easy to understand argument. 例如,许多POSIX标准化函数采用某种
struct
,将所有必要的值累积到一个易于理解的参数中。
No, this can't be done. 不,这不可能。 But you can assign the
NULL
values to the variables and then pass them as parameters if it helps with your readability! 但是您可以将
NULL
值分配给变量,然后将它们作为参数传递,如果它有助于您的可读性!
g_spawn_async(working_directory, argv, envp,flags,child_setup , user_data, child_pid, error);
The BOOST parameter library can help you. BOOST参数库可以帮助您。 It works well and is portable.... See http://www.boost.org/doc/libs/1_54_0/libs/parameter/doc/html/index.html
它运行良好且便于携带....参见http://www.boost.org/doc/libs/1_54_0/libs/parameter/doc/html/index.html
It's certainly possible. 这当然是可能的。 It's not even particularly difficult, but it does involve a lot of code.
它甚至不是特别困难,但确实涉及很多代码。 Something like the following could be used:
可以使用以下内容:
enum MyFuncParamId
{
myA,
myB,
myC,
myD,
unknown
};
class MyFuncParam
{
union OneParam
{
double aOrB;
C c;
int d;
OneParam() {}
~OneParam() {}
};
OneParam myParam;
MyFuncParamId myId;
public:
MyFuncParam( MyFuncParamId id, double value )
: myId( id )
{
switch ( myId ) {
case myA:
case myB:
myParam.aOrB = value;
break;
case myC:
assert( 0 );
abort();
case myD:
myParam.d = value;
break;
}
}
MyFuncParam( MyFuncParamId id, C const& value )
: myId( id )
{
switch ( myId ) {
case myA:
case myB:
case myD:
assert( 0 );
abort();
case myC:
new (&myParam.c) C( value );
break;
}
}
MyFuncParam( MyFuncParamId id, int value )
: myId( id )
{
switch ( myId ) {
case myA:
case myB:
myParam.aOrB = value;
break;
case myC:
assert( 0 );
abort();
case myD:
myParam.d = value;
break;
}
}
MyFuncParam( MyFuncParam const& other )
: myId( other.myId )
{
switch ( myId ) {
case myA:
case myB:
myParam.aOrB = other.myParam.aOrB;
break;
case myC:
new (&myParam.c) C( other.myParam.c );
break;
case myD:
myParam.d = other.myParam.d;
break;
}
}
~MyFuncParam()
{
switch( myId ) {
case myC:
myParam.c.~C();
break;
}
}
MyFuncParam& operator=( MyFuncParam const& ) = delete;
friend class MyFuncParamGroup;
};
class MyFuncRouter
{
MyFuncParamId myId;
public:
MyFuncRouter( MyFuncParamId id ) : myId( id ) {}
MyFuncParam operator=( double value )
{
return MyFuncParam( myId, value );
}
MyFuncParam operator=( C const& value )
{
return MyFuncParam( myId, value );
}
MyFuncParam operator=( int value )
{
return MyFuncParam( myId, value );
}
};
static MyFuncRouter a( myA );
static MyFuncRouter b( myB );
static MyFuncRouter c( myC );
static MyFuncRouter d( myD );
struct MyFuncParamGroup
{
bool aSet;
bool bSet;
bool cSet;
bool dSet;
double a;
double b;
C c;
int d;
MyFuncParamGroup()
: aSet( false )
, bSet( false )
, cSet( false )
, dSet( false )
{
}
void set( MyFuncParam const& param )
{
switch ( param.myId ) {
case myA:
assert( !aSet );
aSet = true;
a = param.myParam.aOrB;
break;
case myB:
assert( !bSet );
bSet = true;
b = param.myParam.aOrB;
break;
case myC:
assert( !cSet );
cSet = true;
c = param.myParam.c;
break;
case myD:
assert( !dSet );
dSet = true;
d = param.myParam.d;
break;
}
}
};
void
myFunc(
MyFuncParam const& p1,
MyFuncParam const& p2,
MyFuncParam const& p3,
MyFuncParam const& p4)
{
MyFuncParamGroup params;
params.set( p1 );
params.set( p2 );
params.set( p3 );
params.set( p4 );
std::cout << "a = " << params.a
<< ", b = " << params.b
<< ", c = " << params.c
<< ", d = " << params.d
<< std::endl;
}
Notes: 笔记:
I've used C++11 here. 我在这里使用过C ++ 11。 The same thing can be done in earlier versions of C++, by replacing the type
C
in the union
with unsigned char c[sizeof( C )];
同样的事情可以在早期版本的C ++来完成,通过更换型
C
在union
与unsigned char c[sizeof( C )];
, adding something to the union
to ensure correct alignment (if necessary), and a lot of type casting. ,向
union
添加一些东西以确保正确对齐(如果需要),以及大量的类型转换。
This would be a lot simpler with boost::variant
(instead of the union
) and boost::optional
(in MyFuncParamGroup
). 使用
boost::variant
(而不是union
)和boost::optional
(在MyFuncParamGroup
)会更简单。 I didn't have Boost available, so I did most of what they do explicitly. 我没有Boost可用,所以我做了他们明确做的大部分工作。 (Which of course, makes the code a lot longer.)
(当然,这会使代码更长。)
I've intentionally used two parameters with the same type, and a user defined type ( C
) with non-trivial constructors, to show how these are handled. 我故意使用两个具有相同类型的参数,以及一个用户定义的类型(
C
)和非平凡的构造函数,以显示如何处理这些参数。
You'd probably want a bit more encapsulation, and a bit more error checking. 你可能想要更多的封装,以及更多的错误检查。
But the real question is: do you really want to go this route? 但真正的问题是:你真的想走这条路吗? The amount of code increases linearly with the number of parameters.
代码量随参数数量线性增加。 And with any decent editor, you can temporarily put the parameter list from the function declaration to the right of your screen, and fill out the parameters to the left, directly along side of the parameter declaration.
使用任何不错的编辑器,您可以暂时将函数声明中的参数列表放在屏幕右侧,并直接在参数声明的旁边填写左侧的参数。 (In gvim, I'll usually use block editing mode for this, but
:vsplit
can also be used.) (在gvim中,我通常会使用块编辑模式,但是
:vsplit
也可以使用:vsplit
。)
Named parameters are very useful and I was even considering that in a language they should be the only way to call a function except for a single obvious parameter if that is present. 命名参数非常有用,我甚至认为在一种语言中,它们应该是调用函数的唯一方法,除非有一个明显的参数,如果存在的话。
sin(x) // the obvious main parameter
sin(x, unit=DEGREE) // any other must be named
C++ unfortunately doesn't have them. 不幸的是,C ++没有它们。
More importantly C++ lacks the metaprogramming ability needed to be able to implement them. 更重要的是,C ++缺乏能够实现它们所需的元编程能力。
While there are tricks that can try to somewhat mimic named parameters and even if the resulting code can look almost reasonable, what is totally indecent is the code you need to write to get that simulation and that there is no way in C++ to generate that code. 虽然有一些技巧可以尝试在某种程度上模仿命名参数,即使结果代码看起来几乎合理,但完全不雅的是你需要编写代码以获得该模拟,并且C ++中没有办法生成该代码。
What I mean is that while it's possible to write a function accepting named parameters emulated reasonably, the code for the function itself is hideous and cannot be automatically generated in C++. 我的意思是虽然可以编写一个接受合理模拟的命名参数的函数,但函数本身的代码是可怕的,不能在C ++中自动生成。 This means that no one will write functions that way so the feature is still not present.
这意味着没有人会以这种方式编写函数,因此该函数仍然不存在。
My guess is that named parameters are missing because of a mix of ignorance (they existed way before C++ was invented, probably even before C) and of pride (even in C++11 the language metaprogramming abilities are pathetic and things trivial like say enumerating at compile time the members of a structure or the parameters of a function is impossible). 我的猜测是,由于无知的混合(它们在C ++发明之前存在,甚至可能在C之前存在)和骄傲(即使在C ++ 11中,语言元编程能力是可悲的,而且事情是微不足道的,比如说枚举,因此缺少命名参数在编译时,结构的成员或函数的参数是不可能的)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.