C++20:将参数包编码和解码为变量,供 varargs 使用

C++20: Encode and decode parameter pack as a variable to be used by varargs

提问人:pion 提问时间:7/26/2023 最后编辑:pion 更新时间:7/26/2023 访问量:128

问:

不确定在经过长时间和详尽的搜索后,C++ 20 中是否可以进行以下操作,但无论如何都会问:

使用 C++2 而不是 C ,我可以:(1) 将传递给可变参数函数模板的任意参数列表编码为单个变量,以便 (2) 该变量可以传递给不同的非模板化函数,在那里它被 (3) 解码并最终被类似函数使用? va_listprintf()

也就是说,我希望能够写一些类似于以下内容的内容:

/*
 * main.cpp
 */
#include <very_old_library.h>
#include <utility>

typedef  /*** what goes here? ***/  AmazingVariadicArgumentEncoderType;

/* Impl26() is NOT templated and accepts a FIXED number of arguments */
void Impl26(const char *fmt, AmazingVariadicArgumentEncoderType encodedArgs)
{
    /*
     *  Decode encodedArgs here and represent them as decodedArgs (maybe?)
     */
    
    VeryOldLibraryFunctionWithPrintfSemantics(fmt, decodedArgs... /* not sure what syntax to use for decodedArgs here */);
}

template <typename... Args>
void Wrapper(const char *fmt, Args&&... args)
{
    AmazingVariadicArgumentEncoderType encodedArgs;
    
    encodedArgs = 
    /*
     *  Encode args here and represent them as encodedArgs.
     *
     *  Possibly using:
     *      std::forward<Args>(args) ...
     *  ?
     */
    
    Impl26(fmt, encodedArgs);
}

int main(int argc, char *argv[])
{
    // Each of these is a valid invocation of Wrapper().
    Wrapper(kOpaqueSecretString1, -123);
    Wrapper(kOpaqueSecretString2, "jenny", 8675309, 'q', argv[1]);
    Wrapper(kOpaqueSecretString3);
    
    return 0;
}


/*
 * very_old_library.h
 */
const char *kOpaqueSecretString1;
const char *kOpaqueSecretString2;
const char *kOpaqueSecretString3;
void VeryOldLibraryFunctionWithPrintfSemantics(const char *fmt, ...);

(当然,上面的代码是我试图解决的实际问题的提炼版本。

我正在努力寻找合适的定义以及编码/解码的步骤。AmazingVariadicArgumentEncoderType

为了清楚起见,重述我的问题(谢谢@Nelfeal):

问:如何编写一个非模板函数来接受单个参数并将其作为多个参数传递给 printf() 之类的东西?


以下是我希望人们会建议对我不起作用的事情:

  1. 使用 std::format 和/或 std::iostream/std:cout

    我实际上并没有打印到 .我的代码专门与一个库接口,该库公开了一个类似 API 的 API,该 API 使用 C 样式的 var args,并且该库无法替换。stdoutprintf()

  2. Impl26() 设为函数模板。

    在我的实际代码中,是一个虚拟成员函数,因此无法模板化。Impl26()

  3. #include < stdarg.h>

    我知道我也可以在我的新代码中使用旧的 C 样式变量,但我想看看是否有可能避免这样做。这既是一个实验和学习练习,也是我试图解决的一个真正问题。

  4. 编码为 std::tuple

    我想不出一种方法可以在不制作函数模板的情况下传递任意类型的任意变量。tupleImpl26()

  5. 重新实现类似 printf() 函数的格式字符串解析逻辑。

    我想要直接传递,而不是试图猜测底层库的实现细节。

C++ 参数传递 C++20 模板 变量 函数

评论

0赞 Nelfeal 7/26/2023
我有点不清楚实际问题在哪里。您是否只想编写一个非模板函数,该函数接受单个参数并将其作为多个参数传递给类似的东西?printf
0赞 pion 7/26/2023
@Nelfeal是的,没错
1赞 Nelfeal 7/26/2023
我认为这是不可能的,除非你想重新实现类似 printf 函数的“类型解码”部分(所以检查和东西)。fmt%d
0赞 pion 7/26/2023
@Nelfeal 谢谢!我也见过这种方法,但忘记将其添加到我的“不需要的解决方案”列表中。我现在已经添加了它。
0赞 Nelfeal 7/26/2023
无论是什么,它都必须丢失有关参数包的所有类型信息。在这一点上,它相当于一个 ,你可以把它传递给适当的函数(如 vprintf),但这只是你列表的第 3 点。AmazingVariadicArgumentEncoderTypeva_list

答:

4赞 chrysante 7/26/2023 #1

用于在已知模板参数的上下文中调用:std::functionVeryOldLibraryFunctionWithPrintfSemantics

/*
 * main.cpp
 */
#include <utility>
#include <functional>

using EncoderCallback = void(*)(char const*, ...);

using AmazingVariadicArgumentEncoderType = std::function<void(EncoderCallback, char const*)>;

void Impl26(const char *fmt, AmazingVariadicArgumentEncoderType encodedArgs) {
    encodedArgs(VeryOldLibraryFunctionWithPrintfSemantics, fmt);
}

template <typename... Args>
void Wrapper(const char *fmt, Args&&... args) {
    AmazingVariadicArgumentEncoderType encodedArgs = [=](EncoderCallback callback, char const* fmt) {
        callback(fmt, args...);
    };
    Impl26(fmt, encodedArgs);
}

int main(int argc, char *argv[])
{
    // Each of these is a valid invocation of Wrapper().
    Wrapper(kOpaqueSecretString1, -123);
    Wrapper(kOpaqueSecretString2, "jenny", 8675309, 'q', argv[1]);
    Wrapper(kOpaqueSecretString3);
    
    return 0;
}