-1

I have a function like this:

typedef std::vector<std::map<int,std::string>> MyVec1;
typedef std::vector<std::map<std::string,std::string>> MyVec2;

enum options { DEFAULT_OPTIONS                   = 0x0000000u,
               OTHERS                            = 0x0000001u };

void func(std::string s1, MyVec1& vec1, MyVec2& vec2, bool type,
                      std::string s2, options opt, uint32_t opt2){
//do something
//And put data to the vectors
}

I want to create a luncher which can take any number of arguments (upto 7) and set default values for the arguments that are not passed and then call the func() with all arguments, kinda like this:

void launcher(args...){
//If any string is passed, pass it as the 1st arg of func, or an empty string
//If any second string is passed, pass it as the 5th arg of func or an empty string
//If any MyVec1 type is passed, pass it as the second arg or a dummy vector.
//If any MyVec2 type is passed, pass it as the third arg or a dummy vector.

////By dummy vector I mean create a vector with MyVec1 vec; and pass it as arg.

////Like this, if other args are passed they are passed to the func with 
////given value otherwise a default value is chosen
////And finally call func() with all arguments.
}

The same can be achieved by overloading and using default values for arguments but that's a huge task if you consider that I want those arguments to be non-sequential too (specially the vectors) i.e launcher will manage if you pass MyVec2 first and MyVec1 as the second.

I had to create 16 overload for a three vector function (3-vector: 6, 2-vector: 6, 1-vector: 3, 0-vector: 1).

Is this even possible?

Note:

  1. I think I can detect the type with this. But the rest seems kinda impossible to me.
  2. If it is not possible to work with reference then that will do too. I have other functions that needs the same which doesn't work with references (A function with lots of args like the above without those vectors).
Community
  • 1
  • 1
Jahid
  • 21,542
  • 10
  • 90
  • 108
  • 1
    Check out `std::bind`. – Captain Obvlious Dec 28 '15 at 00:25
  • how about using void pointers and casting them. unsafe and bad way of doing it. But variable arguments are also as unsafe and bad. Other than doing this kind of hacky things, I can't think of any elegant solution as of now. – Sreekar Dec 28 '15 at 00:26
  • @CaptainObvlious I think I can call std::bind inside the launcher. But how do I go inside it. I need to get all the arguments first (to check their type), I don't have any idea how to go about that. Variadic template with multiple type? I don't know if it is doable with variadic template. – Jahid Dec 28 '15 at 00:44
  • @Sreekar: it wont work. In C it would, in C++ if you define 2 functions with the same name but differents parameters, you will only reference one of the 2 functions, and will never call the 2nd. – Pierre Emmanuel Lallemant Dec 28 '15 at 00:46
  • @Sreekar If you could give an example that would be good. – Jahid Dec 28 '15 at 00:46
  • 4
    Yes, this is possible and fairly straightforward; you basically need a function that gets the `I`-th `T` out of a tuple, or a default-constructed `T` if it doesn't exist. But I'd suggest doing something like https://isocpp.org/wiki/faq/ctors#named-parameter-idiom; it's a hell lot easier to maintain (and read). Also the naive implementation won't allow for conversions, and it's unclear at best what's the best way to handle an argument that can convert to multiple types (by C++'s rules, `const char*` converts better to `bool` than to `std::string`). – T.C. Dec 28 '15 at 00:58
  • @T.C. That's quite interesting... Can I pass a vector (by reference) like that and retain it's changed state? – Jahid Dec 28 '15 at 01:32
  • Sure, just store a pointer in the parameter object and dereference it. – T.C. Dec 28 '15 at 09:23
  • Do you really have to use `func()` like that? Isn't that API written by yourself? I can't belive this is a interface a real library is using, `std::vector>` already makes me puke, and this long list of arguments, where some seems to be redundant, is one of the worst I ever saw. I think you're riding a dead horse. – Superlokkus Dec 28 '15 at 15:22
  • 1
    [Demo](http://ideone.com/foauYb) with pitfalls pointed by T.C. – Jarod42 Dec 29 '15 at 04:18
  • @Superlokkus `std::vector>` already makes me puke, why is that? – Jahid Dec 29 '15 at 08:15
  • @Superlokkus and the only redundant one I can think of is the `bool type`, I can pass it with `options`. `opt` and `opt2` can't be mixed. It depends on another library which has it's own set of options. – Jahid Dec 29 '15 at 08:24
  • @Jahid I couldn't believe that this is the interface of a actual C++ library, so I guesses that you didn't really needed a vector of maps and also the type of the maps were sounding crude to me (not using another typedefs, using `int` in modern C++, a lot of strings, looked to me "stringly typed". But I want to apologies, it's likely not your fault, you have to deal wit this. What library is this? Reminds me a bit to the Win32 interface. – Superlokkus Dec 29 '15 at 11:16
  • @Superlokkus `(not using another typedefs,` I am actually using typedefs for the maps too (though I didn't include them here). There are actually three vectors and all of them needs to be the same i.e users should be able to access them with the same approach as they contain same type of data. And I need map because value needs to be accessed both by name and index; and also index needs to be accessed by name too (the third map). If you have any better idea to do it without vectors and maps, let me know. – Jahid Dec 30 '15 at 07:10
  • @Superlokkus : `using int in modern C++` What do you mean by this? I am using `unsigned long long` typedefed to `Uint` in my actual code. – Jahid Dec 30 '15 at 07:13
  • @Jahid But why are you using `unsigned long long`? You can only be sure that it's at least 16 bit wide, why don't you use the exact or least width types? And why don't you use meaning full typedefs? Whats the reason behind a global `Uint`? Why not something like `typedef uint_least64_t process_id`? – Superlokkus Dec 30 '15 at 12:35
  • @Superlokkus : sorry to say, but `process_id` is meningless in this context. Uint is used for the size of the vectors and it's index count. Precisely speaking I intend to use `size_t` for indexing in the maps and vectors and the size of them. And the width doesn't matter, only the range. `size_t` will be more than enough – Jahid Dec 30 '15 at 13:18
  • @Superlokkus : And also, users won't need any typedefs other than those vectors and maps. All others are just for internal use. – Jahid Dec 30 '15 at 13:33
  • @Jahid As I said I'm sorry, the most disturbing thing for me is `func` which I assume, is not your fault. With this impression I tend to criticize also things, which aren't that bad, but at least worth a discussion :-). But I guess you're right, it's hard to know from this minimized, generalized example (which is good for the original question of course), how you're API will be used. – Superlokkus Dec 31 '15 at 14:38

2 Answers2

1

A naive implementation of what you suggested is actually fairly straightforward - you basically need something that gets the Ith T out of a tuple, or some default value if it doesn't exist. Add some forwarding pixie dust and you are done.

But it's going to be hard to maintain, and there are thorny design issues with arguments that require a conversion. "foo" by C++'s overload resolution rules converts better to bool than to std::string. Do you want to follow those rules or design your own?

Instead, do something like the named parameters idiom:

struct launcher {
    std::string s1 = "";
    MyVec1* p_vec1 = nullptr;
    MyVec2* p_vec2 = nullptr;
    bool type = false;
    std::string s2 = "";
    options opt = DEFAULT_OPTIONS;
    uint32_t opt2 = 0;
    launcher& set_s1(std::string& s) { s1 = s; return *this; }
    launcher& set_vec1(MyVec1& vec1) { p_vec1 = &vec1; return *this; }
    // etc.
    void launch(){
        MyVec1 vec1; MyVec2 vec2;
        MyVec1& v1 = p_vec1 ? *p_vec1 : vec1;
        MyVec2& v2 = p_vec2 ? *p_vec2 : vec2;
        return func(s1, v1, v2, type, s2, opt, opt2);
    }
};
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • The only differences between `struct` and `class` in C++, is the default visibility (public in structs, private in classes). – Superlokkus Dec 29 '15 at 11:09
0

I assume your intention behind launcher is to deliver a more useable interface to func. Based on this, I wouldn't rely just one a bunch of overloads or templates. You could push a little work to the runtime, but simplify the interface a lot by using simple built in pointers.

void launcher(std::string* s1, MyVec1* vec1, MyVec2* vec2, bool* type,
                  std::string* s2, options* opt, uint32_t* opt2){
//is s1 set?
if (s1 != nullptr) // if(s1) would also be ok
/* Construct the default parameters based on what you get*/
/* not enough parameters to call func?*/
throw std::invalid_argument("You need to specify at least ...");

You could also replace the built-in pointers with boost::optional, since it's more clear what your intentions are and boost also makes some optimizing. You could mix this runtime checks, with overloading and templates, but I guess, launcher will not be called in a loop for scientific calculations, isn't it? And your goal is a simple interface.

Superlokkus
  • 4,731
  • 1
  • 25
  • 57