0

I have a small problem at hand. Suppose there is a if condition with only 2 operands but I want to make the operation dynamic.

void somFunc()
{
  if(a && b) /*1*/
  {
  }
  else if(a1 && b1) /*2*/
  {
  }
  else if(a || b) /*3*/
  {
  }
  else if(a1 || b1) /*4*/
}


Basically, 1 and 3 exactly has same parameters with different operation,Similarly for 2 and 4. I want to reduce these 4 operations to 2.
I want to know if there is a way I can make oper dynamic. Consider we only have 2 operations && and ||
Can I use templates in any way ?
If someone wants to know why I need this is, there are n if conditions inside a big if/else. If I somehow achieve this, I reduce the conditions by half.

unbesiegbar
  • 471
  • 2
  • 7
  • 19
  • 3
    You can not introduce specific keywords like `oper` here, but you could make a function `oper()` that takes two parameters and return a `boolean`. If you want to go even further you can give that function as a parameter to `somFunc` to make it even more generic – Petok Lorand Dec 18 '18 at 09:14
  • @Damien. I understand your point. A bit of background to my problem is that this is a small and constrained rule engine where rules are defined externally including the operation. Sample condition will be if (X exists) and (Y not exists). or something like if (X exists) or (Y exists). – unbesiegbar Dec 18 '18 at 09:37
  • @Ajit Thanks for clarification. In this case the two answers already provided should work fine. – Damien Dec 18 '18 at 09:41

5 Answers5

4

Not sure if this is what you are asking for, but you can write something like this:

enum OPERATION { AND, OR }; 

bool operation(bool a, bool b,OPERATION op) {
    if (op == AND) return a && b;
    return a || b;
}    

void somFunc(OPERATION op)
{
    if(operation(a,b,op)) 
    {
    }
}

Or as suggested in a comment, make the operation to be performed a parameter of the function, like so

template <OPERATION> 
void somFunc(OPERATION op)
{
    if(op(a,b)) 
    {
    }
}

and call it like this

somFunc( [](bool a, bool b) { return a && b; });
somFunc( [](bool a, bool b) { return a || b; });
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
1

You can use pointers to funtions.

#include <iostream>
#include <functional>


bool oper1(bool a, bool b) {
    return a || b;
}


bool oper2(bool a, bool b) {
    return a && b;
}

int main() {
    bool a = true, b = false;
    auto oper = oper1;
    if (oper(a, b)) {
        std::cout << "OR\n";
    }
    oper = oper2;
    if (oper(a, b)) {
        std::cout << "AND\n";
    }
}

First you define all your conditions and later you can switch the condition by setting the variable.

You can also use inheritance and functors:

#include <iostream>
#include <functional>
#include <memory>

class Operator {
public:
    virtual bool eval(bool a, bool b) = 0;
};

class OrOperator : public Operator {
public:
    bool eval(bool a, bool b) {
        return a || b;
    }
};

class AndOperator : public Operator {
public:
    bool eval(bool a, bool b) {
        return a && b;
    }
};

class VariableOperator : public Operator {
public:
    VariableOperator(bool val) : val(val) {}
    bool eval(bool a, bool b) {
        return val;
    }
private:
    bool val;
};

int main() {
    bool a = true, b = false;
    std::unique_ptr<Operator> oper(new OrOperator);
    if (oper->eval(a, b)) {
        std::cout << "OR\n";
    }
    oper.reset(new AndOperator);
    if (oper->eval(a, b)) {
        std::cout << "AND\n";
    }
    oper.reset(new VariableOperator(true));
    if (oper->eval(a, b)) {
        std::cout << "VARIABLE\n";
    }
}
Thomas Sablik
  • 16,127
  • 7
  • 34
  • 62
1

You might be looking for something like this:

void somFunc()
{
  std::vector< std::function< bool(bool, bool) > > operators = {
    [](bool a, bool b){ return a && b; },
    [](bool a, bool b){ return a || b; }
  };
  for ( auto& op : operators )
  {
      if ( op( a, b ) )
      {
      }
      else if ( op( a1, b1 ) )
      {
      }
  }
}

You can add more operators or change the parameter types easily enough.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
1

You can do this with CRTP too:

#include <iostream>
#include <string>
#include <memory>

template<class T>
class Operation
{
public:
   bool eval(bool a, bool b)
   {
      return this->impl().eval(a,b);
   }
private:
   T& impl() { return static_cast<T&>(*this); }
};

class AndOperation : public Operation<AndOperation>
{
public:
   bool eval(bool a, bool b)
   {
      return a && b;
   }
};

class OrOperation : public Operation<OrOperation>
{
public:
   bool eval(bool a, bool b)
   {
      return a || b;
   }
};

int main()
{
  AndOperation andOp;
  auto anonOp = std::make_unique<OrOperation>();
  std::cout << andOp.eval(true, true) << std::endl;
  std::cout << anonOp->eval(false,false);
}

see live example here

What are the advantages of CRTP over virtual inheritance? CRTP is a case of static polymorphism. Here's some references:

Compile time vs run time polymorphism in C++ advantages/disadvantages

What is the motivation behind static polymorphism in C++?

C++: How is this technique of compile-time polymorphism called and what are the pros and cons?

The cost of dynamic (virtual calls) vs. static (CRTP) dispatch in C++

Moia
  • 2,216
  • 1
  • 12
  • 34
0

It is possible to make somFunc() a template, and accept any function that accepts two arguments and returns a value that can be tested with if.

 #include <functional>    // for binary operations in std

 template<class Operation> void somfunc(Operation oper)
 {
      if (oper(a,b))
      {
            // whatever
      }
 }

 int main()
 {
      somFunc(std::logical_and<int>());
      somFunc(std::logical_or<int>());
      somFunc(std::plus<int>());         //  addition

       //   pass a lambda

      somFunc([](int a, int b) -> int {return a + b;});   // lambda form of addition
 }

In the above, I've assumed the variables a and b (which have been used in the question, but types unspecified) are of type int.

Peter
  • 35,646
  • 4
  • 32
  • 74