【问题标题】:variadic macro to generate a vector of strings from a collection of unknown parameters可变参数宏,用于从一组未知参数中生成字符串向量
【发布时间】:2016-12-11 12:58:52
【问题描述】:

目前我正在研究一个类似于 C# 中的 String.Format(...) 函数的函数,只是在 C++ 中。 (String.Format(...))

但这不是我的问题。该函数工作正常,但问题在于它需要一个vector<string> 作为参数,如果我想使用一个整数作为参数,我必须编写如下代码:

// function prototype, the function body is not relevant here
string format(string str, vector<string> variables);

// ... some context
  // i could use to_string() here,
  // but imagine a complex type which only overrides the stream operator
  int a = 20; 
  stringstream ss;
  ss << a;
  string a_str = format("a has the value '{}'", { ss.str() });

那是相当多的样板代码!

因此,我需要一个函数,将 unknown 数据类型的集合转换为 vector&lt;string&gt;

我尝试了一些这样的事情:

vector<string> vec_string(vector<void*> args) {
    vector <string> result;

    for (unsigned i = 0; i < args.size(); i++)
    {
        stringstream ss;

        // I can't dereference an object without knowing to pointer type. :(
        ss << *((int*)args[i]);

        result.push_back(ss.str());
    }

    return result;
}

// ... some context

int a = 10;
cout << format("some int: '{}'", vec_string({ (void*) &a }));

这显然只适用于整数并且非常不舒服。我觉得这样做的唯一方法是可变参数宏,但我不知道它们是如何工作的。

here 是我的format(...) 方法的链接。 很抱歉我的拼写错误,但我已尽力改正它。

【问题讨论】:

  • 查看std::tuplestd::initializer_liststd::forward可以做什么。
  • 可变参数宏在这里 IMO 是个坏主意,请查看可变参数模板以了解 C++ 类型系统中的类似功能。
  • 我现在正在阅读this,整个事情似乎比我想象的要复杂得多。

标签: c++ macros string-formatting variadic


【解决方案1】:

这可以通过可变参数模板相对轻松地完成:

template <class T>
auto toString(T&& t) {
    std::stringstream s;
    s << std::forward<T>(t);
    return s.str();
}

template <class... T>
auto toStringVector(T&&... args) {
    std::vector<std::string> res {toString(std::forward<T>(args))...};
    return res;
}

这将通过stringstream 将每个参数转换为std::string,然后返回包含所述字符串的std::vector&lt;std::string&gt;。 (Live example.)

然后您可以按照问题中的意图直接使用它,即:

std::cout << format("some text", toStringVector(any, number, of, arguments,
                                      of, any, type));

如果您使用的是 Boost,则可以跳过 toString 帮助程序以支持 boost::lexical_cast

template <class... T>
auto toStringVector(T&&... args) {
    std::vector<std::string> res { boost::lexical_cast<std::string>(std::forward<T>(args))...};
    return res;
}

lexical_cast 在内置类型上很可能会更快。

【讨论】:

  • 啊,在我自己弄清楚之前,您只用了 30 秒就回答了我的问题。 (显然你的答案更好......)谢谢!
【解决方案2】:

我想通了,不知道我在第一次尝试时是如何做到的 - 没有编译器错误,但我是这样做的:

// function prototype, the function body is not relevant here
string format(string str, vector<string> variables);

template <class T>
vector<string> paramsToString(vector<string> vec, T last) {
    stringstream ss;
    ss << last;
    vec.push_back(ss.str());

    return vec;
}

template <class T, class ... REST>
vector<string> paramsToString(vector<string> vec, T next, REST ... rest) {
    stringstream ss;
    ss << next;
    vec.push_back(ss.str());

    return paramsToString(vec, rest...);
}

template <class ... ARGS>
vector<string> paramsToString(ARGS ... args) {
    return paramsToString(vector<string>(), args ...);
}

// ... some context

// ComplexType overrides the stream operator.
cout << format("an int: '{0}', and string: '{1}' and some other type: '{2}'", 
  paramsToString(10, "Hello World", ComplexType(10))); 

而且它有效!即使使用自定义类型。太棒了!

感谢你们的帮助!

【讨论】:

  • 整洁。不过,您有一些不必要的副本,请参阅我的回答,了解如何避免这些。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-22
  • 2017-02-02
  • 2014-09-01
  • 2020-12-30
  • 2017-05-02
  • 2011-09-17
  • 1970-01-01
相关资源
最近更新 更多