Passing operator as a parameter

22,638

Solution 1

Declare:

template<class Func> bool myfunc(char lv, char rv, Func func);

Or if you need to link it separately:

bool myfunc(char lv, char rv, std::function<bool(bool,bool)> func);

Then you can call:

myfunc('t', 'f', std::logical_or<bool>());

Solution 2

@ybungalobill posted a C++ correct answer and you should stick to it. If you want to pass the operators, functions will not work, but macros would do the work:

#define MYFUNC(lv, rv, op) ....

// Call it like this
MYFUNC('t', 'f', ||);

Be careful, macros are evil.

Solution 3

What you can do is define proxy operators that return specific types.

namespace detail {
    class or {
        bool operator()(bool a, bool b) {
            return a || b;
        }
    };
    class and {
        bool operator()(bool a, bool b) {
            return a && b;
        }
    };
    // etc
    class X {
        or operator||(X x) const { return or(); }
        and operator&&(X x) const { return and(); }
    };
};
const detail::X boolean;
template<typename T> bool myfunc(bool a, bool b, T t) {
     return t(a, b);
}
// and/or
bool myfunc(bool a, bool b, std::function<bool (bool, bool)> func) {
    return func(a, b);
}
// example
bool result = myfunc(a, b, boolean || boolean);

You can if desperate chain this effect using templates to pass complex logical expressions.

Also, the XOR operator is bitwise, not logical- although the difference is realistically nothing.

However, there's a reason that lambdas exist in C++0x and it's because this kind of thing flat out sucks in C++03.

Solution 4

In modern C++ can pass any operator by using lambdas.
Update 1: the proposed solution introduces small improvement which is suggested by @HolyBlackCat

#include <iostream>

template<class T, class F> void reveal_or(T a, T b, F f)
{
    // using as function(a, b) instead of expression a || b is the same thing
    if ( f(a, b) ) 
        std::cout << a << " is || " << b << std::endl;
    else
        std::cout << a << " is not || " << b << std::endl;

}

template<class T> void reveal_or(T a, T b)
{
    // reuse the already defined ||
    reveal_or(a, b, [](T t1, T t2) {return t1 || t2; });
}

Don't bother how to pass parameter if || operator is defined

int main ()
{
    reveal_or('1', 'a');
    return 0;
}

Passing explicitly as parameter. We can pass anything, including including any exotic nonsense

int main ()
{
    //same as above:
    reveal_or('1', 'a', [](char t1, char t2) { return t1 || t2; });
    //opposite of above
    reveal_or('1', 'a', [](char t1, char t2) { return !( t1 || t2; ) });

    return 0;
}
Share:
22,638
nacho4d
Author by

nacho4d

C/C++/Objective-C/C#/Javascript/shell-script/Japanese/Go/Spanish/English I love this site. So Helpful! https://twitter.com/nacho4d http://nacho4d-nacho4d.blogspot.com/

Updated on September 27, 2021

Comments

  • nacho4d
    nacho4d over 2 years

    I want to have a function that evaluates 2 bool vars (like a truth table).

    For example:

    Since

    T | F : T
    

    then

    myfunc('t', 'f', ||);  /*defined as: bool myfunc(char lv, char rv, ????)*/
    

    should return true;.

    How can I pass the third parameter?

    (I know is possible to pass it as a char* but then I will have to have another table to compare operator string and then do the operation which is something I would like to avoid)

    Is it possible to pass an operator like ^ (XOR) or || (OR) or && (AND), etc to a function/method?

  • Billy ONeal
    Billy ONeal over 13 years
    Correct me if I'm wrong, but isn't overloading operator|| and operator&& evil?
  • Billy ONeal
    Billy ONeal over 13 years
    This isn't true. STL functors are typically not function pointers.
  • Puppy
    Puppy over 13 years
    @Billy: No- why would it be? This is no different to the _1 and etc in boost::bind.
  • Billy ONeal
    Billy ONeal over 13 years
    Sorry... didn't see that those were inside classes :P Thought you were breaking short circuit semantics for every expression containing && or ||!
  • ltjax
    ltjax over 13 years
    Overloading operator|| and operator&& is evil (because such overloads are no longer lazy and evaluate their params in undefined order) unless they are used for expression templates (where that does not matter) like in this case.
  • erinus
    erinus over 13 years
    I think that STL functors is also an object, so it also has a memory address to send into function parameter.
  • Konrad Rudolph
    Konrad Rudolph over 13 years
    that’s irrelevant. You can overload operators and pass them to functions. It just so happens that this cannot be done (for no good reasons) with pre-defined operators of built-in types. So passing operators to functions definitely works – just not in this very particular case (unless you use the trick shown by DeadMG, then it even works for this case).
  • erinus
    erinus over 13 years
    Thanks for Konrad. I don't know this. And I wanna know that: overload operator -> create class's RTTI extra info for the operator, so for compiler, it's not a default operator(compiler make it with default binary code). overload parameter will compile to reference RTTI to know how operator handle its action. so it can pass the reference, and default operator can't pass. Is it right?(I'm not good at English. Sorry.)
  • Brett Rossier
    Brett Rossier almost 13 years
    Just thought it'd be helpful to include a link to more function objects like std::logical_or cplusplus.com/reference/std/functional
  • Patrick
    Patrick over 4 years
    @ybungalobill I tried to implement myfunc as return func(int(lv), rv(lv)). But calling it like myfunc('1', '3', std::plus<int>()); then returns just 1 instead of 4. Isn't that meant to be used like that?
  • Yakov Galka
    Yakov Galka over 4 years
    @Patrick: myfunc returns bool, so it cannot return 4. Also '1' == 49 and '3' == 51, so the answer I would expect is 100 == 'd'.
  • HolyBlackCat
    HolyBlackCat about 3 years
    Having std::function as a default argument saves typing, but gives you extra overhead compared to a plain lambda. I'd write two overloads, one with 3 params and no default args, and the other with two parameters, calling the first one.