简体   繁体   English

如何一起使用C ++和C可变参数?

[英]How can C++ and C variadic arguments be used together?

Generally, using the C++11 variadic template feature with functions requires the variadic-based function arguments to be the last in the function argument list. 通常,在函数中使用C ++ 11可变参数模板功能要求基于可变参数的函数参数是函数参数列表中的最后一个。 There is one exception; 有一个例外; they are the next-to-last arguments if there are C-level variadic arguments, which must be dead last. 如果存在C级可变参数,它们是倒数第二个参数,它们必须是最后的。

template < typename ...Args >
int  super_printf( Something x, Args &&...a, ... );

I sometimes randomly think about C++, and I wondered how such a function can be implemented. 我有时会随机思考C ++,我想知道如何实现这样的功能。 I first thought about the usual recursive peeling of arguments from a , then I remembered that the C-level varargs don't cascade. 我首先想到了通常从a递归剥离的论点,然后我记得C级varargs没有级联。 I have to turn them to a definitive va_list right away. 我必须马上把它们变成一个明确的va_list。

template < typename ...Args >
int  super_vaprintf( Something x, std::va_list &aa, Args &&...a );
// Note that "aa" is passed by reference.

template < typename ...Args >
int  super_printf( Something x, Args &&...a, ... )
{
    std::va_list  args2;
    int           result;

    va_start( args2, XXX );  // (A)
    try {
        result = super_vaprintf( x, args2, std::forward<Args>(a)... );
    } catch ( ... ) {
        va_end( args2 );  // (1)
        throw;
    }
    va_end( args2 );  // (2)
    return result;

    // Can (1) and (2) be compacted with RAII using a custom deleter lambda
    // in std::unique_ptr or something?  Remember that "va_end" is a macro!
}

The usual C++ variadic recursive peeling happens in the super_vaprintf call. 通常的C ++可变参数递归剥离发生在super_vaprintf调用中。 At line (A), what goes in the place of XXX , "a" or "a..."? 在(A)行,代替XXX ,“a”或“a ......”的是什么? What happens if a is empty, does x go there instead? 如果a为空会发生什么, x会转到那里吗? If that last question's true, are we screwed if there's no x ; 如果最后一个问题是真的,如果没有x ,我们是否会被搞砸; that there's no arguments besides the variadic ones? 除了可变参数之外没有其他参数? (And if it's true, how do we conditionalize the code to use x when a is empty, and a otherwise?) (如果这是真的,我们如何实现条件使用X如果是空的, 并且否则代码?)

... ...

I just looked at my copy of the C++11 standard for any assistance here. 我在这里查看了我的C ++ 11标准副本以获得任何帮助。 There doesn't seem to be any. 似乎没有。 This would prompt a request for the C++ committee to come back to fix this, but I'm not sure that there's any way such a function could be called without the C++ varargs taking everything. 这会提示请求C ++委员会回来解决这个问题,但是我不确定在没有C ++ varargs占用所有内容的情况下可以调用这样的函数。 Am I wrong; 我错了吗; can a function call be made to use both C++ and C varargs? 可以调用函数来同时使用C ++和C varargs吗? Or is mixing only useful for declarations, in terms of Stupid (Template) Instantiation Tricks? 或者就Stupid(模板)实例化技巧而言,混合仅对声明有用吗?

When you call a function whose last parameter is a pack, all the arguments become part of that pack. 当您调用最后一个参数为pack的函数时,所有参数都将成为该包的一部分。 There is nothing left for va_args . va_args没有任何东西。 Your usage of explicit template arguments is misleading because they are not exclusive; 您对显式模板参数的使用会产生误导,因为它们不是唯一的; they simply precede implicit arguments. 它们只是在隐含的参数之前。

To defeat deduction, you need a reference: 要打败演绎,你需要一个参考:

(& super_printf<int, int>) ( 0L, 1, 2, 3, 4, 5 )

This is fairly contrived, but now you have the problem of nothing to pass to va_start . 这是相当人为的,但现在你有一个问题没有传递给va_start

To provide a reasonable interface to users, just add a parameter between the two lists. 要为用户提供合理的界面,只需在两个列表之间添加参数即可。

struct va_separator {}; // Empty; ABI may elide allocation.

template < typename ...Args >
int  super_printf( Something x, Args &&...a, va_separator, ... );

This super_printf will need both explicit arguments to define the pack and an explicit separator argument. 这个super_printf需要两个显式参数来定义包和一个显式的分隔符参数。 But you can alternatively provide a public function which receives all its arguments by pack, then finds the separator and forwards to super_printf using an explicit argument list comprising the pack elements before the separator. 但是你也可以提供一个公共函数,它通过pack接收它的所有参数,然后找到分隔符并使用包含分隔符之前的pack元素的显式参数列表转发到super_printf

I've tried this code out on a compiling web site (Coliru), with GCC 4.8, and the results look bleak. 我已经在编译网站(Coliru)上使用GCC 4.8尝试了这个代码,结果看起来很暗淡。 I don't know if it's GCC in particular, or if all the other compilers out there do something similar. 我不知道它是否特别是GCC,或者是否所有其他编译器都做了类似的事情。 So can people with other compilers (Clang, Visual C++, Intel, etc.) try this out? 那些与其他编译器(Clang,Visual C ++,Intel等)的人可以试试这个吗?

#include <cstdarg>
#include <iostream>
#include <ostream>
#include <utility>

template < typename ...Args >
int  super_vaprintf( long, std::va_list &, Args &&... )
{
    return 17;
}

template < typename ...Args >
int  super_printf( long x, Args &&...a, ... )
{
    std::va_list  args2;
    int           result;

    va_start( args2, a );  // (A)
    try {
        result = super_vaprintf( x, args2, std::forward<Args>(a)... );
    } catch ( ... ) {
        va_end( args2 );
        throw;
    }
    va_end( args2 );
    return result;
}

int main() {
    std::cout << super_printf<int, int>( 0L, 1, 2, 3, 4, 5 ) << std::endl;  // (B)
    return 0;
}

The call to super_printf on line (B) explicitly sets the C++ varargs to two int entries. 对行(B)上的super_printf的调用显式地将C ++ varargs设置为两个int条目。 This will make the function use arguments 1 and 2 as C++ varargs and the latter three as C varargs. 这将使函数使用参数12作为C ++ varargs,后三个作为C varargs。

On line (A), the compiler insists that the code with a in it have a " ... " somewhere. 在第(A)行,编译器坚持认为其中包含a的代码在某处有“ ... ”。 So I change it to: 所以我把它改成:

va_start( args2, a... );  // (A)

I get another error about having the wrong number of arguments. 关于错误数量的参数,我得到了另一个错误。 It makes sense since a expands to two arguments. 这使得因为感觉a膨胀到两个参数。 If I change line (B) to one C++ vararg: 如果我将行(B)更改为一个C ++ vararg:

std::cout << super_printf<int>( 0L, 1, 2, 3, 4, 5 ) << std::endl;  // (B)

it works just fine. 它工作得很好。 If I remove the C++ varargs entirely: 如果我完全删除C ++ varargs:

std::cout << super_printf<>( 0L, 1, 2, 3, 4, 5 ) << std::endl;  // (B)

we get the wrong-number-or-arguments error again, because a has length of zero). 我们再次得到错误的数字或参数错误,因为a长度为零)。 If we do this when a is empty: 如果我们在a为空时执行此操作:

va_start( args2, x /*a...*/ );  // (A)

the code works again, although there is a warning about x not being the last named parameter. 代码再次起作用,尽管有关于x不是最后命名参数的警告。

We can approach the example in another way. 我们可以用另一种方式来处理这个例子。 Let's reset to: 让我们重置为:

va_start( args2, a... );  // (A)
//...
std::cout << super_printf( 0L, 1, 2, 3, 4, 5 ) << std::endl;  // (B)

where all the arguments after the first are grouped as C++ varargs. 其中第一个之后的所有参数都被分组为C ++ varargs。 We get the same too-many-arguments error in va_start , of course. 当然,我们在va_start得到了相同的多参数错误。 I progressively comment out trailing arguments. 我逐渐评论出尾随的论点。 It works when there are exactly two arguments left (which makes a have exactly one argument). 它的工作原理时,都完全没有留下两个参数(这使得a有一个确切的说法)。

There is also an error when there's only one argument left, but the error message changes to explicitly say "too few" arguments instead of "wrong amount." 当只剩下一个参数时也会出现错误,但错误消息会更改为明确说“太少”参数而不是“错误数量”。 Like before, I switched out " a... " for " x " in line (A), and the code was accepted, but there was no warning. 像以前一样,我在行(A)中为“ x ”切换出“ a... ”,代码被接受,但没有警告。 So it seems that when I explicitly include " <Whatever> " for super_printf in line (B) I get a different parser error path than when I don't include them, although both paths go to the same conclusion. 因此,当我在行(B)中为super_printf明确包含“ <Whatever> ”时,我得到的解析器错误路径与我不包含它们时的路径不同,尽管两条路径都得出相同的结论。

Time to tell the committee that they overlooked something.... 是时候告诉委员会他们忽略了什么......

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

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