1

it's any possibility to use a variadic parameters with out specification of first one?

For example:

This code is perfectly fine:

void something(const char* layoutAndButton, ...)
{
    va_list ap;
    va_start(ap, layoutAndButton);
    std::map<std::string, std::string> data;
    while (*layoutAndButton != '\0') {
        std::string layout = va_arg(ap, const char*);
        ++layoutAndButton;
        std::string button = va_arg(ap, const char*);
        ++layoutAndButton;
        data.insert(std::make_pair(layout, button));
    }
    for (auto const& x : data)
    {
        std::cout << x.first << ':' << x.second << std::endl;
    }
    va_end(ap);
}

But I would like to have the something function in this way:

void something(const char*...)

It's any possibility to do something like that? and after that to access the members? if yes, how?

Thanks

Mircea
  • 31
  • 1
  • 9
  • 8
    How about variadic template instead of old C-ellipsis ? – Jarod42 Nov 13 '17 at 13:56
  • It possible to write `void something(...)` – user7860670 Nov 13 '17 at 13:57
  • This'll be a duplicate of [Variadic function without specified first parameter?](https://stackoverflow.com/questions/1436968/variadic-function-without-specified-first-parameter) or [Function with a variable number of parameters without any explicit parameters](https://stackoverflow.com/questions/18491805/function-with-a-variable-number-of-parameters-without-any-explicit-parameters) or etc. – underscore_d Nov 13 '17 at 13:57
  • Agree with @Jarod42. C++11's variadic templates allows us to write type-safe versions of var args functions. – AndyG Nov 13 '17 at 13:58
  • 1
    BTW, in your case, `void something(std::initializer_list>)` seems even appropriate. – Jarod42 Nov 13 '17 at 13:59
  • @VTT For an old C-style ellipsis function, you can only access the parameter values (portably) if you have at least one non-variadic argument. That makes variadic-only functions fine if you're doing SFINAE tricks, but they're not much use for anything else. – Arthur Tacca Nov 13 '17 at 14:02
  • @Jarod42 could you explain me how to use `void something(std::initializer_list>) ` because I want to access my function something like that `something("Some", "things", "are", "done");` – Mircea Nov 13 '17 at 14:03
  • @underscore_d that's maybe be similar but is not helping me and anyway.... in that question is no answer at all – Mircea Nov 13 '17 at 14:04
  • It would be `something({{"Some", "things"}, {"are", "done"}});` – Jarod42 Nov 13 '17 at 14:04
  • So in conclusion... can anyone please give me an example how to use somthing(...) and no more? but in the same time to access all member from that function? – Mircea Nov 13 '17 at 14:05
  • Why are you iterating over the `layoutAndButton` parameter? You don't seem to be actually using it. – Arthur Tacca Nov 13 '17 at 14:14
  • @ArthurTacca I just try to save that date from (...) part to an map and I have no idea how to do that – Mircea Nov 13 '17 at 14:19
  • @Mircea In that case, even if you do have an initial `layoutAndButton` parameter, you would only need it for the initial `va_start` bit; you can forget about it when doing `va_arg`. – Arthur Tacca Nov 13 '17 at 14:24

3 Answers3

1

Here is how you would use a C++ variadic template. You can call something() with any even number of const char* arguments e.g. something("k1", "v1", "k2", "v2"). It will build a map<string,string> from these arguments by recursively calling the buildMap() function, and then call useMap() to do whatever actual work you want done with the map.

void buildMap(std::map<std::string, std::string>& data) 
{
}

template<typename... Args>
void buildMap(
    std::map<std::string, std::string>& data, 
    const char* layout, 
    const char* button, 
    Args... args) 
{
    data.insert(std::make_pair(layout, button));
    buildMap(data, args...);
}


void useMap(std::map<std::string, std::string>& data) 
{
    // TODO: do something here
}

template<typename... Args>
void something(Args... args) {
    std::map<std::string, std::string> data;
    buildMap(data, args...);
    useMap(data);
}
Arthur Tacca
  • 8,833
  • 2
  • 31
  • 49
1

As state in comment std::initializer_list seems to do the job

void something(std::initializer_list<std::pair<std::string, std::string>> layoutAndButtons)
{
    // std::map<std::string, std::string> m(layoutAndButtons); // potentially
    for (auto const& p : layoutAndButtons) {
        std::cout << p.first << ':' << p.second << std::endl;
    }
}

or even, if you really need a map:

void something(const std::map<std::string, std::string>& layoutAndButtons)
    for (auto const& p : layoutAndButtons) {
        std::cout << p.first << ':' << p.second << std::endl;
    }
}

With usage similar to:

something({{"Some", "things"}, {"are", "done"}});

If you really want variadic template, I suggest:

template<typename... Args>
void something(Args... args) 
{
    static_assert(sizeof...(Args) % 2 == 0, "wrong number of argument");
    const char* layoutAndButtons[] = {args...};

    std::map<std::string, std::string> m;
    for (auto it = std::begin(layoutAndButtons);
         it != std::end(layoutAndButtons);
         it += 2) {
        auto layout = *it;
        auto button = *(it + 1);
        m.emplace(layout, button);
    }
    for (auto const& p : m)
    {
        std::cout << p.first << ':' << p.second << std::endl;
    }
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

If you can use C++14 (std::index_sequence and std::make_index_sequence) you can avoid recursion wrapping your args... in a std::tuple, generating a list of indexes and initializing the std::map with indexes and std::get().

I mean: if you write an helper function as follows

template <typename ... Args, std::size_t ... Is>
std::map<std::string, std::string> getMap
   (std::tuple<Args...> const & t, std::index_sequence<Is...> const &)
 { return { {std::get<(Is<<1)>(t), std::get<(Is<<1)+1U>(t)} ... }; }

in something() you can initialize data as follows

auto data = getMap(std::tie(args...),
                   std::make_index_sequence<(sizeof...(Args)>>1)>{});

but I also suggest to precede this line with a static_assert() to check that the number of args... is even; something like

static_assert( (sizeof...(Args) & 1U) == 0U, "#Args is odd!"); 

The following is a full working example

#include <map>
#include <tuple>
#include <iostream>
#include <type_traits>

template <typename ... Args, std::size_t ... Is>
std::map<std::string, std::string> getMap
   (std::tuple<Args...> const & t, std::index_sequence<Is...> const &)
 { return { {std::get<(Is<<1)>(t), std::get<(Is<<1)+1U>(t)} ... }; }

template <typename... Args>
void something (Args... args)
 {
   static_assert( (sizeof...(Args) & 1U) == 0U, "#Args is odd!");

   auto data = getMap(std::tie(args...),
                      std::make_index_sequence<(sizeof...(Args)>>1)>{});

   for ( auto const & p : data )
      std::cout << '[' << p.first << ',' << p.second << ']';

   std::cout << std::endl;
 }

int main ()
 {
   something("k1", "v1", "k2", "v2", "k3", "v3"); // compile
   //something("k1", "v1", "k2", "v2", "odd!"); // static_assert() failure
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • Although I voted for this – it's definitely interesting – to be honest I found it more confusing than my recursive `getMap()`. – Arthur Tacca Nov 13 '17 at 16:22
  • 1
    @ArthurTacca - because you underestimate the power of the dark side of index generation... well... I mean... yes, can be a little more difficult to understand. – max66 Nov 13 '17 at 16:32