2016-12-11 2 views
2

В настоящее время я работаю над функцией, аналогичной функции String.Format(...), из C#, только на C++. (String.Format(...))variadic macro для генерации вектора строк из коллекции неизвестных параметров

Но это не моя проблема. Функция работает нормально, но проблематично в том, что он принимает 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() }); 

Это совсем немного шаблонный код!

Таким образом, мне нужна функция, которая преобразует коллекцию неизвестных типов данных в vector<string>.

Я попробовал несколько вещей, как это:

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 })); 

который, очевидно, работает только для целых и очень неудобно. Я чувствую, что единственный способ сделать это: variadic macro, но я понятия не имел, как они работают.

here - ссылка на мой метод format(...). Прошу прощения за мое правописание, но я старался исправить его.

+2

Посмотрите, что вы можете сделать с помощью 'std :: tuple',' std :: initializer_list' и 'std :: forward'. –

+1

Матрицы Variadic - плохая идея здесь IMO, проверьте вариационные шаблоны для аналогичной функциональности в системе типа C++. –

+0

Я читаю [это] (https://www.murrayc.com/permalink/2015/12/05/modern-c-variadic-template-parameters-and-tuples/) прямо сейчас, и кажется, что все вещь намного сложнее, чем я думал. –

ответ

2

Это может быть сделано относительно легко с переменным числом шаблонов:

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; 
} 

Это будет преобразовывать каждый параметр в std::string через stringstream, а затем возвращают std::vector<std::string>, содержащим указанные строки. (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 скорее всего будет быстрее на встроенных типах.

+0

Ах, ты ответил на мой вопрос всего за 30 секунд, прежде чем я сам это понял.(очевидно, ваш ответ лучше ...) Спасибо! –

0

Я понял это, не знаю, как я сделал это с первой попытки - без ошибок компилятора, но здесь не то, как я это сделал:

// 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))); 

И это работает! Даже с пользовательскими типами. Удивительно!

Благодарим вас, ребята, за вашу помощь!

+1

Уход. У вас есть некоторые ненужные копии, хотя, см. Мой ответ о том, как их избежать. –

Смежные вопросы