2

let's say i have a function foo, that takes two non-primitive object of different types ,e.g:

void foo(House h, Dog d) {// implementation  }

assuming that placement of these arguments has no effect on the function output then foo(h,d) = foo (d,h) theoretically. but c++ does not allow this unless I overload this function by:

void foo (Dog d, House h) {// implementation   }

but this overloading becomes a pain in the butt if the number of arguments increases (e.g. 6 overloading for 3 arguments and so on).

my question is, is there any easy way to acheive the conveniece of passing arguments not in a specific order without repetative overloading?

14
  • 4
    Just out of curiosity, why would anyone want to pass parameters to a function in a wrong order? Commented Feb 10, 2017 at 5:16
  • Why do you need this flexibility? If you construct it, you can pass them in the defined order. Commented Feb 10, 2017 at 5:20
  • if you know the function takes 3 different type of arguments, wouldn't be convenient to be able pass them in any order rather than checking the function prototyoe? Commented Feb 10, 2017 at 5:20
  • Encapsulate all these arguments in an object and pass the object reference to the function. Commented Feb 10, 2017 at 5:22
  • that's not the point! mathematically speaking if the output of f(x,y) is the same as f(y,x), why should i concern about the correct order of the arguments? Commented Feb 10, 2017 at 5:24

6 Answers 6

3

It's quite possible to re-order n arguments with O(n) wrappers:

#include <iostream>
using namespace std;
struct A { int a; A(int a) : a(a) {} };
struct B { int b; B(int b) : b(b) {} };
struct C { int c; C(int c) : c(c) {} };
struct D { int d; D(int d) : d(d) {} };
static void foo(A a, B b, C c, D d) { cout << a.a << " " << b.b << " " << c.c << " " << d.d << endl; }
template<class ...Args> struct Foo { void operator()(Args...); };
template<class ...Args> static void foo(Args ...args) { Foo<Args...>()(args...); }
template<class T, class U> struct Foo<T, U, C, D> { void operator()(T t, U u, C c, D d) { foo(u, t, c, d); } };
template<class T, class U, class V> struct Foo<T, U, V, D> { void operator()(T t, U u, V v, D d) { foo(v, t, u, d); } };
template<class T, class U, class V, class W> struct Foo<T, U, V, W> { void operator()(T t, U u, V v, W w) { foo(w, t, u, v); } };
int main() {
    foo(A(1), B(2), C(3), D(4));
    foo(D(5), C(6), B(7), A(8));
    return 0;
}

(The wrapper class Foo is required because functions cannot be partially specialized.)

$ c++ -std=c++11 a.cc
$ ./a.out
1 2 3 4
8 7 6 5

Do not construe this as an endorsement of this technique. Rather: even if it's possible, please don't do this.

Sign up to request clarification or add additional context in comments.

Comments

3

In C++17, we have std::get<T>(std::tuple). This can be combined with std::forward_as_tuple:

template< typename ... unordered_arg >
void foo( std::tuple< unordered_arg ... > arg_set ) {
    House h = std::get< House && >( std::move( arg_set ) );
    Dog d = std::get< Dog && >( std::move( arg_set ) );

    // implementation
}

template< typename ... arg >
void foo( arg ... a )
    { foo_impl( std::forward_as_tuple( std::move( a ) ... ) ); }

(It's alright to multiply do move(arg_set) as long as a mutually-exclusive part of it is accessed each time.)

4 Comments

The tuple contains references, and you try to get bare types out. You'll have to write your own get that decays types in the tuple before trying to match them to its parameter for this approach to work.
@yurikilochek Thanks, fixed. (Instead of reimplementing/shimming get, I switched to pass-by-value.)
I really like your answer. The only problem I can see is that it's UB if you have the same type twice because of how std::get works. Am I wrong? This could be a strong constraint.
@skypjack That's a compile-time error according to cppreference. There's no rule for repetitions in the permutations here, so failure by default seems like a good behavior.
1

Generally speaking, it is better to reduce the number of parameters passed to a function. There are two ways to do what you want.

  • Create a class which has these non primitive data as member variables. Once these members are initiliazed, you can call a member function which operates on these member data

  • Encapsulate these non primitive data in an object and pass this object by reference to your function.

Either way you don't need to worry about the order of arguments as long as all arguments are initialized.

4 Comments

i agree that encapsulation might be a solution, but making a class or structure for each stand alone function is just not practical. is it? maybe I am wrong or maybe i asked a bad question.
No, I am not advocating to create a class or structure for each stand alone function. Basically, you need to identify which functions operate on which data and is there a scope to club together these data in an encapsulated format so that you have related member functions operating on the data they are interested in. Also, another thing to note is generally a class or a function should have single responsibility.
In which order do you pass the arguments to the constructor of this class you want him to make?
@user12341234, in such a case, constructor may be used to default construct the members and OP may have to depend on Setter functions to finish initialization.
0

As far as I can tell, there does not seem to be a way to replicate Python-style keyword arguments in C++ (beyond the ways listed in the provided Software Engineering thread.

Comments

0

You can do that using hierarchy to do that as this example

class Object
{
public:
    enum Type { Dog, House};
    Type m_type;
};

class Dog : public Object
{

};

class House : public Object
{

};

void myFunction(const Object& a, const Object& b)
{

}

If needed you can recast the object using m_type within myFunction or implement global function on the parent class.

Another way might be to use templates.

1 Comment

This throws away any notion of compile time safety. You'd need dynamic_casts to retrieve the types.
0

is there any easy way to acheive the conveniece of passing arguments not in a specific order without repetative overloading?

Yes, you can do this, but because you haven't said what is your exact usage of this function I am not able to give you general purpose solution to this. Anyway, for all people claiming in the comments that behavior which is needed by the author of the question is wrong, bad, stupid etc, please consider following piece of code, which works and I see potential in this.

Let's assume that we have a class which has fields which store some values and we want to have a setters (and probably getters as well) to access those values (for the clarity I presented only minimum version).

So, this class would be:

struct A {
    int g1 {};
    int g2 {};
    int g3 {};
};

This class does not have setters or getters because we are going to wrap it in different class, a Data class.

struct Data {
    template <typename... Ts>
    void set (Ts&&... ts) {
        set_impl (std::forward<Ts> (ts)...);
    }

    A a;
private:
    template <typename T>
    void set_impl (T&& t) {
        a.*T::mem = t.v; // (X)
    }

    template <typename T, typename K, typename... Ts>
    void set_impl (T&& t, K&& k, Ts&&... ts) {
        set_impl (std::forward<T> (t));
        set_impl (std::forward<K> (k), std::forward<Ts> (ts)...);
    }
};

So here we have a class which has set member function which takes arbitrary number or arguments and each of them can be (and in this example should be) of different type. To set A fields like you see in (X) you need to pass an object of type which has a pointer to A member. So please take a look at the following classes.

struct G {
    G (int c = {}) : v {c} {}

    int v;
};

struct G1 : G { using G::G; static constexpr int A::* mem = &A::g1; };
struct G2 : G { using G::G; static constexpr int A::* mem = &A::g2; };
struct G3 : G { using G::G; static constexpr int A::* mem = &A::g3; };

All Gx functions are here only for setting the value of A fields and each of them knows which field is going to set.

Helper function for showing the result is:

void show (const A& v) {
    std::cout << "g1 = " << v.g1 << std::endl;
    std::cout << "g2 = " << v.g2 << std::endl;
    std::cout << "g3 = " << v.g3 << std::endl;
}

And finally, the usage:

Data d;
d.set (G1 {10}, G2 {20}, G3 {30});
show (d.a);

Data p;
p.set (G3 {40}, G1 {-30}, G2 {120});
show (p.a);

which gives us the output:

g1 = 10
g2 = 20
g3 = 30
g1 = -30
g2 = 120
g3 = 40

Like I said, I do not know if this fits your needs, but this is just an example how to do this and it maybe helpful for you.

Nonetheless putting values in not specified order (in conjunction with arbitrary number of values) has advantage that you can set only those values that are needed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.