可变参数模板:如何“展望未来”在论据中

发布于 2025-01-10 03:15:34 字数 4015 浏览 0 评论 0原文

我正在实现一个 printf 版本,它也可以处理 std::string 参数。 它的核心有以下功能:

// use sprintf to transform the single format string with the value t
template<typename T>
std::string simpleFormat(const std::string sFormat, const T t) {

    size_t required = snprintf(NULL, 0, sFormat.c_str(), t);
    char sTemp[required+1];
    sprintf(sTemp, sFormat.c_str(), t);
    return std::string(sTemp);
} 

// termination of recursion
std::string recursiveFormat(stringvec &vParts, stringvec &vFormats, uint i) {
    return vParts[i];
}

// recursively walk through arguments 
template<typename T, typename... Args>
std::string recursiveFormat(stringvec &vParts, stringvec &vFormats, uint i, T value, Args... args) {

    std::string sRes = "";
    if (i < vFormats.size()) {
        sRes += vParts[i];
        sRes += simpleFormat(vFormats[i], value);
        sRes += recursiveFormat(vParts, vFormats, i+1, args...);
    }
    return sRes;
}

这非常有效。但是,要处理带有星号的格式字符串,例如在 printf("%0*d", width, value) 中,这种方法不起作用。

我尝试添加两个函数

// get the next argument and return it
template<typename U, typename... Args>
U fetchNextParam(const U value2, Args... args) {
    return value2;
}

// handle the star by providing sprintf with two values
template<typename T, typename U>
std::string starFormat(const std::string sFormat, const T value1, const U value2) {

    size_t required = snprintf(NULL, 0, sFormat.c_str(), value1, value2);
    char sTemp[required+1];
    sprintf(sTemp, sFormat.c_str(), value1, value2);
    return std::string(sTemp);
} 

并修改 recursiveFormat() 函数,如下所示:

template<typename T, typename... Args>
std::string recursiveFormat(stringvec &vParts, stringvec &vFormats, uint i, T value, Args... args) {

    std::string sRes = "";
    if (i < vFormats.size()) {
        sRes += vParts[i];
        //--- trying to handle star
  
        if (vFormats[i].find('*') != std::string::npos) {
            auto value2 = fetchNextParam(args...);
            sRes += starFormat(vFormats[i], value, value2);
            
        } else {
            sRes += simpleFormat(vFormats[i], value);
        } 
        sRes += recursiveFormat(vParts, vFormats, i+1, args...);
    }
    return sRes;
}

但是,此代码无法编译:我收到编译器消息:

sptest.cpp: In instantiation of ‘std::string recursiveFormat(stringvec&, stringvec&, uint, T, Args ...) [with T = double; Args = {}; std::string = std::__cxx11::basic_string<char>; stringvec = std::vector<std::__cxx11::basic_string<char> >; uint = unsigned int]’:
sptest.cpp:114:32:   recursively required from ‘std::string recursiveFormat(stringvec&, stringvec&, uint, T, Args ...) [with T = int; Args = {double}; std::string = std::__cxx11::basic_string<char>; stringvec = std::vector<std::__cxx11::basic_string<char> >; uint = unsigned int]’
sptest.cpp:114:32:   required from ‘std::string recursiveFormat(stringvec&, stringvec&, uint, T, Args ...) [with T = int; Args = {int, double}; std::string = std::__cxx11::basic_string<char>; stringvec = std::vector<std::__cxx11::basic_string<char> >; uint = unsigned int]’
sptest.cpp:124:36:   required from here
sptest.cpp:107:41: error: no matching function for call to ‘fetchNextParam()’
  107 |             auto value2 = fetchNextParam(args...);
      |                           ~~~~~~~~~~~~~~^~~~~~~~~
sptest.cpp:58:3: note: candidate: ‘template<class U, class ... Args> U fetchNextParam(U, Args ...)’
   58 | U fetchNextParam(const U value2, Args... args) {
      |   ^~~~~~~~~~~~~~
sptest.cpp:58:3: note:   template argument deduction/substitution failed:
sptest.cpp:107:41: note:   candidate expects at least 1 argument, 0 provided
  107 |             auto value2 = fetchNextParam(args...);
      |                           ~~~~~~~~~~~~~~^~~~~~~~~

我必须做什么才能使功能看起来“一个”在可变参数中“领先”?

I am implementing a printf version which can also handle std::string arguments.
At the heart of it there are these functions:

// use sprintf to transform the single format string with the value t
template<typename T>
std::string simpleFormat(const std::string sFormat, const T t) {

    size_t required = snprintf(NULL, 0, sFormat.c_str(), t);
    char sTemp[required+1];
    sprintf(sTemp, sFormat.c_str(), t);
    return std::string(sTemp);
} 

// termination of recursion
std::string recursiveFormat(stringvec &vParts, stringvec &vFormats, uint i) {
    return vParts[i];
}

// recursively walk through arguments 
template<typename T, typename... Args>
std::string recursiveFormat(stringvec &vParts, stringvec &vFormats, uint i, T value, Args... args) {

    std::string sRes = "";
    if (i < vFormats.size()) {
        sRes += vParts[i];
        sRes += simpleFormat(vFormats[i], value);
        sRes += recursiveFormat(vParts, vFormats, i+1, args...);
    }
    return sRes;
}

This works very well. However, to handle a format strings with an asterisk, like for instance in printf("%0*d", width, value) this approach doesn't work.

I tried by adding two functions

// get the next argument and return it
template<typename U, typename... Args>
U fetchNextParam(const U value2, Args... args) {
    return value2;
}

// handle the star by providing sprintf with two values
template<typename T, typename U>
std::string starFormat(const std::string sFormat, const T value1, const U value2) {

    size_t required = snprintf(NULL, 0, sFormat.c_str(), value1, value2);
    char sTemp[required+1];
    sprintf(sTemp, sFormat.c_str(), value1, value2);
    return std::string(sTemp);
} 

and modify the recursiveFormat() function like this:

template<typename T, typename... Args>
std::string recursiveFormat(stringvec &vParts, stringvec &vFormats, uint i, T value, Args... args) {

    std::string sRes = "";
    if (i < vFormats.size()) {
        sRes += vParts[i];
        //--- trying to handle star
  
        if (vFormats[i].find('*') != std::string::npos) {
            auto value2 = fetchNextParam(args...);
            sRes += starFormat(vFormats[i], value, value2);
            
        } else {
            sRes += simpleFormat(vFormats[i], value);
        } 
        sRes += recursiveFormat(vParts, vFormats, i+1, args...);
    }
    return sRes;
}

However, this code won't compile: i get the compiler message:

sptest.cpp: In instantiation of ‘std::string recursiveFormat(stringvec&, stringvec&, uint, T, Args ...) [with T = double; Args = {}; std::string = std::__cxx11::basic_string<char>; stringvec = std::vector<std::__cxx11::basic_string<char> >; uint = unsigned int]’:
sptest.cpp:114:32:   recursively required from ‘std::string recursiveFormat(stringvec&, stringvec&, uint, T, Args ...) [with T = int; Args = {double}; std::string = std::__cxx11::basic_string<char>; stringvec = std::vector<std::__cxx11::basic_string<char> >; uint = unsigned int]’
sptest.cpp:114:32:   required from ‘std::string recursiveFormat(stringvec&, stringvec&, uint, T, Args ...) [with T = int; Args = {int, double}; std::string = std::__cxx11::basic_string<char>; stringvec = std::vector<std::__cxx11::basic_string<char> >; uint = unsigned int]’
sptest.cpp:124:36:   required from here
sptest.cpp:107:41: error: no matching function for call to ‘fetchNextParam()’
  107 |             auto value2 = fetchNextParam(args...);
      |                           ~~~~~~~~~~~~~~^~~~~~~~~
sptest.cpp:58:3: note: candidate: ‘template<class U, class ... Args> U fetchNextParam(U, Args ...)’
   58 | U fetchNextParam(const U value2, Args... args) {
      |   ^~~~~~~~~~~~~~
sptest.cpp:58:3: note:   template argument deduction/substitution failed:
sptest.cpp:107:41: note:   candidate expects at least 1 argument, 0 provided
  107 |             auto value2 = fetchNextParam(args...);
      |                           ~~~~~~~~~~~~~~^~~~~~~~~

What must i do to get the functionality to look "one ahead" in the variadic arguments?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

べ繥欢鉨o。 2025-01-17 03:15:35

您还可以提供fetchNextParam(/*空参数*/)

// it's type need to return something since you store the value into value2
int fetchNextParam(){throw std::runtime_error("Wrong number of argument");}

You can also provide fetchNextParam(/*empty parameter*/)

// it's type need to return something since you store the value into value2
int fetchNextParam(){throw std::runtime_error("Wrong number of argument");}
魔法唧唧 2025-01-17 03:15:34

仅当有足够的参数时,您才可以使用 if constexpr (C++17) 来激活代码:

if (vFormats[i].find('*') != std::string::npos) {
    if constexpr (sizeof...(Args) > 0) {
        auto value2 = fetchNextParam(args...); // std::get<0>(std::tie(args...))
        sRes += starFormat(vFormats[i], value, value2);
    } else {
        throw std::runtime_error("Wrong number of argument");
    }
} else {
    sRes += simpleFormat(vFormats[i], value);
}

You might use if constexpr (C++17) to activate code only if there is enough parameter:

if (vFormats[i].find('*') != std::string::npos) {
    if constexpr (sizeof...(Args) > 0) {
        auto value2 = fetchNextParam(args...); // std::get<0>(std::tie(args...))
        sRes += starFormat(vFormats[i], value, value2);
    } else {
        throw std::runtime_error("Wrong number of argument");
    }
} else {
    sRes += simpleFormat(vFormats[i], value);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文