-1

I want to have a combine function which takes multiple parameters in any order. for example:

int combine(int a, float b, char c)
{
    return a+b+c;
}

this is just an example of what I want to do, but I want to be able to call this function with any order of arguments:combine(1,2.5,'c') or combine('c', 1 2.5) or combine(2.5,1,'c') because creating a function for every case is tedious especially when you have a lot of arguments and you want those in any order. is this possible?

gturri
  • 13,807
  • 9
  • 40
  • 57
user1477
  • 111
  • 1
  • 6

6 Answers6

6

You could use a function template:

template <typename T1, typename T2, typename T3>
int combine(T1 a, T2 b, T3 c)
{
    return a+b+c;
}

Note that this would a, b, and c to be any types for which the addition expression a+b+c is valid.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • a function template means i can use any type,I want to use only combine(int,float,char) so integer floats and chars in this case i want another function to have combine(Vector, Point, something) so this is a general solution to any arguments but i want 3 specific ones in any order – user1477 Dec 29 '13 at 16:32
  • @user1477 then you can use SFINAE. C++11 provides many helpful templates in [``](http://en.cppreference.com/w/cpp/header/type_traits). It is not trivial though. – juanchopanza Dec 29 '13 at 16:34
  • SFINAE seems to be what i was looking for – user1477 Dec 29 '13 at 16:43
5

You can make combine() a function template

template <typename T0, typename T1, typename T2>
int combine(T0 a0, T1 a1, T2 a2) {
    return a0 + a1 + a2;
}

If you want to restrict the types which can be passed, you can use SFINAE to add constraints. Restricting the arguments to be of type char, int, and float, especially if standard conversions should be allowed, isn't entirely trivial, though. I'd think it would look something like this:

#include <type_traits>

template <typename T>
struct is_allowed
    : std::integral_constant<bool,
          std::is_convertible<T, char>::value
          || std::is_convertible<T, int>::value
          || std::is_convertible<T, float>::value> {
};
template <typename T0, typename T1, typename T2>
typename std::enable_if<
    is_allowed<T0>::value && is_allowed<T1>::value && is_allowed<T2>::value,
int>::type combine(T0 a0, T1 a1, T2 a2) {
    return a0 + a1 + a2;
}

In case you actually want to retain fractional parts, too, if present, you might want to look at std::common_type<T0, T1, T2>::type for the return type.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • this seems to be the answer im looking for one second im researching it – user1477 Dec 29 '13 at 16:35
  • Its traits allow `(char, char, char)` or `(float, float, float)` though. – Jarod42 Dec 29 '13 at 16:52
  • that's not a problem in my case but i can see why this could be a problem – user1477 Dec 29 '13 at 16:56
  • @Jarod42: correct. Making sure there is exactly one of each is a bit more fun. Also, it actually deduces `double` and `long` etc. and don't convert them to the set of more fundamental types. Making this happen would still be more fun but whether it is needed is something I don't know. – Dietmar Kühl Dec 29 '13 at 16:58
0

you have 2 ways:

1-you can overload the function -> it is a bad way

2-you can use template -> i recommend it

Moamen
  • 11
  • 2
0

You could overload the function but have a single "base" function which gets called in the others, like:

// "base" function:
int combine(int a, float b, char c)
{
    return a+b+c;
}

// define "forwarders" for reordered function types:
inline int combine(float b, int a, char c) { return combine(a, b, c); }
inline int combine(int a, char c, float b) { return combine(a, b, c); }
inline int combine(float b, char c, int a) { return combine(a, b, c); }
inline int combine(char c, int a, float b) { return combine(a, b, c); }
inline int combine(char c, float b, int a) { return combine(a, b, c); }

As you told us in the comments, you want to further overload this with another combination of three types; hence you can consider wrapping those five lines which define the overloads in a macro:

#define OVERLOAD_REORDERED(Treturn, functionName, T1, T2, T3)                \
    inline Treturn functionName(T2 b, T1 a, T3 c) { return functionName(a, b, c); } \
    inline Treturn functionName(T1 a, T3 c, T2 b) { return functionName(a, b, c); } \
    inline Treturn functionName(T2 b, T3 c, T1 a) { return functionName(a, b, c); } \
    inline Treturn functionName(T3 c, T1 a, T2 b) { return functionName(a, b, c); } \
    inline Treturn functionName(T3 c, T2 b, T1 a) { return functionName(a, b, c); } \

and then only write:

int combine(int a, float b, char c)
{
    return a+b+c;
}
OVERLOAD_REORDERED(int, combine, int, float, char)

Since you probably split this into declarations (.h) and definitions (.cpp), you can put the macro call in the header, like this:

// .h:
int combine(int a, float b, char c);
OVERLOAD_REORDERED(int, combine, int, float, char)

// .cpp:
int combine(int a, float b, char c)
{
    return a+b+c;
}
leemes
  • 44,967
  • 21
  • 135
  • 183
  • imagine you have 6 arguments, this becomes impossible as it is the square of the number of arguments – user1477 Dec 29 '13 at 16:40
  • Yes, but I doubt you can get any better solution. C++ does not support "unordered" arguments. – leemes Dec 29 '13 at 16:41
0

Nobody mentioned it in the other answers, so I'll just reiterate what I said in the comment. You want to build something from a group of objects, the order of which is unspecified. I think you'd do well with a variant of the builder pattern

You don't need an abstract builder in your case. Just have an accumulator objects with a bunch of add member functions for the types you want to support. Add'em in a loop and then just get the result from the accumulator.

If your types really are just primitives integral and floating point values, simply write out the loop and do the addition directly.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
0

Following may help:

it uses template and SFINAE.

this SFINAE is to have each type exactly once.

template <typename T, typename ...Ts> struct count_type;

template <typename T, typename Tail, typename ...Ts>
struct count_type<T, Tail, Ts...>
{
    constexpr static int value = std::is_same<T, Tail>::value + count_type<T, Ts...>::value;
};
template <typename T> struct count_type<T>
{
    constexpr static int value = 0;
};

template <typename T0, typename T1, typename T2>
typename std::enable_if<count_type<int, T0, T1, T2>::value == 1
               && count_type<float, T0, T1, T2>::value == 1
               && count_type<int, T0, T1, T2>::value == 1, int>::type
combine(T0 a0, T1 a1, T2 a2) {
    return a0 + a1 + a2;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302