How to avoid copy and paste when two functions are very similar? - c++

I often come across methods that have the same structure and logic, but with some differences and I don't find a proper way not to repeat myself.
For example :
void ChannelSelection::selectAlmostOkChannels(int currentInkId)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
if (tmpStatus == Assessment::Ok)
selected = false;
else if (tmpStatus == Assessment::NotOk)
m_autoSelection[report.name].setSelected(currentInkId, false);
}
}
m_currentSelection.insert(currentInkId, selected);
}
void ChannelSelection::selectNotOkChannels(int currentInkId)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
if (tmpStatus == Assessment::Ok || tmpStatus == Assessment::AlmostOk)
selected = false;
}
}
m_currentSelection.insert(currentInkId, selected);
}
As you can see, these 2 functions are very similar (only the inner if differs). How could I nicely remove duplication in this code?
One of the solution I thought of is using a functor, something like :
void ChannelSelection::selectChannels(int currentInkId, std::function<bool()> fn)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
selected = fn();
}
}
m_currentSelection.insert(currentInkId, selected);
}
The caller gets the responsibility to implement the functor. Is there an alternative without that problem?

You don't have to make your parameterized selectChannels public. It can be a private implementation detail of both selectAlmostOkChannels and selectNotOkChannels, your public functions.
selectChannels can even be implemented as a function template, so that the generated code is equivalent to the hand-written "copy pasted" version, without the maintenance burden of code duplication
template<typename SelectFunction>
void ChannelSelection::selectChannels(int currentInkId, SelectFunction selectFn)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
selected = selectFn(tmpStatus);
/* fill in */
}
}
m_currentSelection.insert(currentInkId, selected);
}
void ChannelSelection::selectAlmostOkChannels(int currentInkId)
{
selectChannels(currentInkId, [] (auto tmpStatus) -> bool {
return /* fill in */;
});
}
void ChannelSelection::selectNotOkChannels(int currentInkId)
{
selectChannels(currentInkId, [] (auto tmpStatus) -> bool {
return /* fill in */;
});
}
You might have been taught that templates need to be in headers, but that's actually not the full story! Template definitions need to be visible where instantiated. Since your template is only used in the private implementation of your member functions, then your template definition can be in the same file that's implementing both your member functions

You might merge two functions into a single one with additional conditional parameter like:
void ChannelSelection::selectChannels(int currentInkId, bool condition)
{
bool selected = true;
foreach (auto report, m_reports) {
if (report.scoreByInk.find(currentInkId) != report.scoreByInk.end()) {
auto tmpStatus = Assessment::getStatusFromScore(report.scoreByInk.value(currentInkId));
if (condition) {
if (tmpStatus == Assessment::Ok) {
selected = false;
} else if (tmpStatus == Assessment::NotOk) {
m_autoSelection[report.name].setSelected(currentInkId, false);
}
} else if (tmpStatus == Assessment::Ok || tmpStatus == Assessment::AlmostOk) {
selected = false;
}
}
}
m_currentSelection.insert(currentInkId, selected);
}
Calling it with condition == true will invoke the equivalent of selectAlmostOkChannels() function, and selectNotOkChannels() otherwise.

Related

How to change this code from recursion to iteration

I've trying to make the following code implement by iteration instead of recursive(the "hit" function of the BVH_node class),but I've no idea how to do this.I did found some topic about this,but they are not very helpful.Could anybody help me?thanks so much.(by the way this is the third time I ask this question ,people says that I'm not making my question clear,please give me some advice on asking question)
class Ray {
public:
vec3 origin;
vec3 direction;
float t = 10000.0f;
};
class AABB{
public:
bool hit(Ray ray){
//test if ray hit itself
//but don't write the t into the ray
}
vec3 min;
vec3 max;
};
class Geometry {
public:
bool hit(Ray& ray) {
float t;
//test if the ray hit this geometry
//ray.origin + t * ray.direction = ......
//then solve the t
if (t < ray.t && t>0.0f) {
ray.t = t;
return true;
}
return false;
}
private:
//...some private data
};
class BVH_node {
public:
void construction(/*......some data for construction*/) {
//is_node_or_geo = ......
//if(is_node_or_geo == 0)
//L_node = ......; R_node = ......;
//if(is_node_or_geo == 1)
//L_geo = ......; R_geo = ......;
}
bool hit(Ray& ray) {
if(aabb.hit(ray)){
if (is_node_or_geo == 0) {
bool hit_left = L_node->hit(ray);
bool hit_right = R_node->hit(ray);
return hit_left || hit_right;
}
if (is_node_or_geo == 1) {
bool hit_left = L_geo->hit(ray);
bool hit_right = R_geo->hit(ray);
return hit_left || hit_right;
}
}
return false;
}
private:
int is_node_or_geo;//node = 0,geo = 1
AABB aabb;
BVH_node* L_node;
BVH_node* R_node;
Geometry* L_geo;
Geometry* R_geo;
};
int main(){
BVH_node* root = new BVH_node;
root->construction(/*.......*/);
Ray ray;//ray = .......;
bool hit = root->hit(ray);
delete root;
}
most tutorial I found on how to change recursion to iteration(like this http://blog.moertel.com/posts/2013-05-11-recursive-to-iterative.html) don't consider some situation where member variable is involved.
Follow up on the approach mentioned in a comment of Alan Birtles:
Let us focus on the hit function. In order to make our life easier, it's better to first rewrite it as follows:
bool hit(Ray& ray) {
if(!aabb.hit(ray)) return false;
if (is_node_or_geo == 0) {
if (L_node->hit(ray)) return true;
if (R_node->hit(ray)) return true;
return false;
} else if (is_node_or_geo == 1) {
if (L_geo->hit(ray)) return true;
if (R_geo->hit(ray)) return true;
return false;
}
}
Now we can add a loop and a stack (implemented with a std::vector, since I'm not familiar with std::stack). We also make the method static, as it does not relate to a single object:
static bool hit(Ray& ray, BVH_node * root) {
std::vector<BVH_node *> nodes {root};
while (!nodes.empty()){
BVH_node & top = *nodes.back();
nodes.pop_back();
if(!top.aabb.hit(ray)) continue;
if (top.is_node_or_geo == 0) {
nodes.push_back(top.L_node);
nodes.push_back(top.R_node);
continue;
} else if (top.is_node_or_geo == 1) {
if (top.L_geo->hit(ray)) return true;
if (top.R_geo->hit(ray)) return true;
continue;
}
}
return false;
}
This code wasn't tested so it might require minor tweacking.
Note that depending on your implementation, you might want to add a bunch of consts - after every mention of BVH_node

Is there an elegant way to traverse Clang AST Statements?

I am trying to traverse all function definitions and extract information from them. I have to iterate over all statements in the function body, and depending on the type, execute a specific function.
For the moment I have an ugly if-else block. Is there a more elegant way to do this?
void FunctionMatcher::processStatement(const clang::Stmt *statement) {
string type = statement->getStmtClassName();
if (type == "ReturnStmt") {
auto rs = dyn_cast<const ReturnStmt *>(statement);
processReturnStmt(rs);
} else if (type == "WhileStmt") {
auto ws = dyn_cast<WhileStmt>(statement);
processWhileStmt(ws);
} else if (type == "ForStmt") {
auto fs = dyn_cast<const ForStmt *>(statement);
processForStmt(fs);
} else if (type == "IfStmt") {
auto is = dyn_cast<const IfStmt *>(statement);
processIfStmt(is);
} else if (type == "SwitchStmt") {
auto ss = dyn_cast<const SwitchStmt *>(statement);
processSwitchStmt(ss);
} else if (type == "CompoundStmt") {
auto cs = dyn_cast<const CompoundStmt *>(statement);
for (auto child : cs->children())
processStatement(child);
} else {
// ...
}
By navigating through the code of clang::TextNodeDumper, I found a solution.
Apparently Clang has its own visitors for statements, declarations etc...
Simple example:
class StatementVisitor : public ConstStmtVisitor<StatementVisitor> {
public:
StatementVisitor();
void Visit(const Stmt *Node) {
ConstStmtVisitor<StatementVisitor>::Visit(Node);
}
void VisitIfStmt(const IfStmt *Node) {
llvm::outs() << " An if statement yay!\n";
}
void VisitWhileStmt(const WhileStmt *Node) {
llvm::outs() << " A While statement yay!\n";
}
};
you can use RecursiveASTVisitor
its traverse all statement in given code, recursively
class MyASTVisitor : public RecursiveASTVisitor<MyASTVisitor>
{
public:
bool VisitFunctionDecl(FunctionDecl* f)
{
...
}
bool VisitIfStmt(IfStmt* IF)
{
...
}
bool VisitForStmt(ForStmt* FS)
{
...
}
bool VisitWhileStmt(WhileStmt* WS)
{
...
}
}

Refactoring needed for repetitious switch statements

The many repetitious switch statements seems like it needs to be DRY'd. Any suggestions? (Including doing nothing!)
AnimMapIter _iter;
_iter = _animations->find(name);
if(_iter == _animations->end()) return;
if(_curName != name) {
_curName = name;
switch(dir) {
case DIR_FORWARD_LOOPING: /* Fall through to DIR_FORWARD_NONLOOPING */
case DIR_FORWARD_NONLOOPING:
_iter->second->First();
break;
case DIR_REVERSE_LOOPING: /* Fall through to DIR_REVERSE_NONLOOPING */
case DIR_REVERSE_NONLOOPING:
_iter->second->Last();
break;
}
} else {
switch(dir) {
case DIR_FORWARD_LOOPING: /* Fall through to DIR_FORWARD_NONLOOPING */
case DIR_FORWARD_NONLOOPING:
_iter->second->Next();
break;
case DIR_REVERSE_LOOPING: /* Fall through to DIR_REVERSE_NONLOOPING */
case DIR_REVERSE_NONLOOPING:
_iter->second->Previous();
break;
}
switch(dir) {
case DIR_FORWARD_LOOPING:
if(_iter->second->IsAtEnd())
_iter->second->First();
break;
case DIR_FORWARD_NONLOOPING:
if(_iter->second->IsAtEnd())
_iter->second->Last();
break;
case DIR_REVERSE_LOOPING:
if(_iter->second->IsAtFront())
_iter->second->Last();
break;
case DIR_REVERSE_NONLOOPING:
if(_iter->second->IsAtFront())
_iter->second->First();
break;
}
}
Everything under the else should collapse into a single switch to bring the related steps closer; e.g.
case DIR_FORWARD_LOOPING:
_iter->second->Next();
if (_iter->second->IsAtEnd()) {
_iter->second->First();
}
break;
...all in that one case. Repetition of a couple of function calls is not a big deal when it makes the overall sequence of actions more clear.
Push the logic into whatever _iter->second is, along these lines (assuming the methods you've already shown exist):
class WhateverItIs
{
public:
void Start() { if (m_forward) First(); else Last(); }
void Stop() { if (m_forward) Last(); else First(); }
void Advance()
{
if (m_forward)
Next();
else
Previous();
if (IsLast())
{
if (m_loop)
Start();
else
Stop();
}
}
private:
bool IsLast() const
{
return m_forward ? IsAtEnd() : IsAtFront();
}
// Direction and looping are independent concepts.
bool m_forward;
bool m_loop;
};
Then you can write:
AnimMapIter _iter;
_iter = _animations->find(name);
if(_iter == _animations->end()) return;
if(_curName != name) {
_curName = name;
_iter->second->Start();
} else {
_iter->second->Advance();
}
Edit: Example using free functions and keeping the constants.
void Start(Strip* s, bool forward)
{ if (forward) s->First(); else s->Last(); }
void Stop(Strip* s, bool forward)
{ if (forward) s->Last() else s->First(); }
void Advance(Strip* s, bool forward, bool loop)
{
if (forward)
s->Next();
else
s->Previous();
if (IsLast(s, forward))
{
if (loop)
Start(s);
else
Stop(s);
}
}
bool IsLast(const Strip* s, bool forward) const
{
return forward ? s->IsAtEnd() : s->IsAtFront();
}
bool Projector::IsForward() const
{
return dir == DIR_FORWARD_LOOPING || dir == DIR_FORWARD_NONLOOPING;
}
bool Projector::IsLooping() const
{
return dir == DIR_REVERSE_LOOPING || dir == DIR_FORWARD_LOOPING;
}
if(_curName != name) {
_curName = name;
Start(_iter->second, IsForward());
} else {
Advance(_iter->second, IsForward(), IsLooping());
}

Anderson tree problem

Thought I'd use an Anderson tree for something. So I started porting to C++ the Julienne Walker version found here: http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_andersson.aspx
Now I have insertions working. But the problem is if I compile with optimisations it crashes. Even -O1 crashes it.
template <class Tv>
class AaTree
{
private:
template <typename Tdata>
struct AaNode
{
AaNode()
{
level = 0;
link[0] = 0L;
link[1] = 0L;
}
~AaNode()
{}
int level;
Tdata data;
AaNode<Tdata>* link[2];
};
AaNode<Tv>* root;
AaNode<Tv>* nil; // sentinel
inline AaNode<Tv>* make_node(Tv data, int level)
{
AaNode<Tv>* rn = new AaNode<Tv>();
rn->data = data;
rn->level = level;
rn->link[0] = rn->link[1] = nil;
}
inline AaNode<Tv>* skew(AaNode<Tv>* t)
{
if (t->link[0]->level == t->level && t->level != 0)
{
AaNode<Tv>* save = t->link[0];
t->link[0] = save->link[1];
save->link[1] = t;
t = save;
}
return t;
}
inline AaNode<Tv>* split(AaNode<Tv>* t)
{
if (t->link[1]->link[1]->level == t->level && t->level != 0)
{
AaNode<Tv>*save = t->link[1];
t->link[1] = save->link[0];
save->link[0] = t;
t = save;
++t->level;
}
return t;
}
AaNode<Tv>* _insert(AaNode<Tv>* root, Tv data)
{
if (root == nil)
root = make_node(data, 1);
else {
AaNode<Tv>* it = root;
AaNode<Tv>* path[64];
int top=0, dir=0;
for (;;)
{
path[top++] = it;
dir = it->data < data;
if (it->link[dir] == nil)
break;
it = it->link[dir];
}
it->link[dir] = make_node(data, 1);
while (--top >= 0)
{
if (top != 0)
dir = path[top - 1]->link[1] == path[top];
path[top] = skew(path[top]);
path[top] = split(path[top]);
if ( top != 0 )
path[top - 1]->link[dir] = path[top];
else
root = path[top];
}
}
return root;
}
void _print(AaNode<Tv>* root)
{
if (root != nil)
{
_print(root->link[0]);
printf("level(%d): %d\n", root->level, root->data);
_print(root->link[1]);
}
}
public:
AaTree()
: root(0L)
{
nil = new AaNode<Tv>();
root = nil;
}
~AaTree()
{}
void Insert(Tv data)
{
root = _insert(root, data);
}
void Delete(Tv data)
{
root = _remove(root, data);
}
void Print()
{
_print(root);
}
};
int main(int argc, char* argv[])
{
AaTree<int> tree;
for (int i = 0; i < 100; i++)
tree.Insert(i);
tree.Print();
return 0;
}
Your make_node function claims to return a value, but contains no return statement.
struct AaNode should not be a template in your case. Try to remove it and see what will happen.
struct AaNode
{
AaNode()
{
level = 0;
link[0] = 0L;
link[1] = 0L;
}
~AaNode()
{}
int level;
Tv data;
AaNode* link[2];
};
But in any case make_node() must return a value. I don't know how you even able to compile this.
Use a proper construction initializer list:
AaNode()
:
level(0),
data(),
link()
{
link[0] = 0L;
link[1] = 0L;
}
Remove the inline keywords from your functions. Inline should be reserved for very small functions (general guideline is one or two lines max), the functions you are attempting to inline are too large and will most likely be more inefficient than calling normally.
Your sentinal value nil should probably be const static. Also it wouldn't hurt to initialize it with some easily recognisable value which may aid in debugging.
In skew() and split() you are not doing any checking to make sure that t is valid or that t's links are valid before dereferencing the pointer.
As others have noted, your make_node does not return the node it creates.
In insert your for loop doesnt check to make sure its not accessing out of bounds memory (> 64th entry of path)

How do I refactor code into a subroutine but allow for early exit?

There's a really obvious refactoring opportunity in this (working) code.
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
// Match against the ni codes of the rule
if(rule.get_ni1() == *iter)
{
// If there's a match, check if it's flagged include or exclude
const std::string flag = rule.get_op1();
// If include, code is included unless a later rule excludes it
if(flag == "INCLUDE"){ included = true; }
// If exclude, code is specifically excluded
else if(flag == "EXCLUDE"){ return false; }
}
if(rule.get_ni2() == *iter)
{
const std::string flag = rule.get_op2();
if(flag == "INCLUDE"){ included = true; }
else if(flag == "EXCLUDE"){ return false; }
}
if(rule.get_ni3() == *iter)
{
const std::string flag = rule.get_op3();
if(flag == "INCLUDE"){ included = true; }
else if(flag == "EXCLUDE"){ return false; }
}
if(rule.get_ni4() == *iter)
{
const std::string flag = rule.get_op4();
if(flag == "INCLUDE"){ included = true; }
else if(flag == "EXCLUDE"){ return false; }
}
if(rule.get_ni5() == *iter)
{
const std::string flag = rule.get_op5();
if(flag == "INCLUDE"){ included = true; }
else if(flag == "EXCLUDE"){ return false; }
}
}
return included;
}
I want to turn it to something like:
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
// Match against the ni codes of the rule
included |= matchNICode(rule.get_ni1(), rule.get_op1);
included |= matchNICode(rule.get_ni2(), rule.get_op2);
included |= matchNICode(rule.get_ni3(), rule.get_op3);
included |= matchNICode(rule.get_ni4(), rule.get_op4);
included |= matchNICode(rule.get_ni5(), rule.get_op5);
}
return included;
}
bool Translations::matchNICode(const std::string& ni,
const std::string& op)
{
if(ni == *iter)
{
if(op == "INCLUDE"){ return true; }
else if(op == "EXCLUDE"){ /*Return worse than false*/ }
}
return false;
}
The problem is that I can't get around the problem that I want to exit early if it's an exclude statement.
Note that I can't change the structure of the Rule class.
Any advice?
One possible refactoring is the below, but I'm not sure if it is worth the trouble
#define NI_CLAUSE(ID) \
if(rule.get_ni ## ID() == *iter) \
{ \
const std::string flag = rule.get_op ## ID(); \
if(flag == "INCLUDE"){ included = true; } \
else if(flag == "EXCLUDE"){ return false; } \
}
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
NI_CLAUSE(1)
NI_CLAUSE(2)
NI_CLAUSE(3)
NI_CLAUSE(4)
NI_CLAUSE(5)
}
return included;
}
I assume that the get_niX() or get_opX() methods have some kind of side effect; otherwise, as soon as you get a true, you would want to exit.
If the thing returned from matchNICode() is really worse than false, it may be an exception. In this case, it is quite simple:
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
try
{
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
// Match against the ni codes of the rule
included |= matchNICode(rule.get_ni1(), rule.get_op1);
included |= matchNICode(rule.get_ni2(), rule.get_op2);
included |= matchNICode(rule.get_ni3(), rule.get_op3);
included |= matchNICode(rule.get_ni4(), rule.get_op4);
included |= matchNICode(rule.get_ni5(), rule.get_op5);
}
return included;
}
catch (WorseThanFalseException& ex)
{
return false; // Or whatever you have to do and return
}
}
bool Translations::matchNICode(const std::string& ni,
const std::string& op)
{
if(ni == *iter)
{
if(op == "INCLUDE"){ return true; }
else if(op == "EXCLUDE"){ throw WorseThanFalseException(); } // Whatever this is
}
return false;
}
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
struct
{
RULE_GET_NI get_ni;
RULE_GET_OP get_op;
} method_tbl[] =
{
{ &Rule::get_ni1, &Rule::get_op1 },
{ &Rule::get_ni2, &Rule::get_op2 },
{ &Rule::get_ni3, &Rule::get_op3 },
{ &Rule::get_ni4, &Rule::get_op4 },
{ &Rule::get_ni5, &Rule::get_op5 },
};
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
for(size_t n = 0; n < 5 /* I am lazy here */; ++n)
{
if((rule.*(method_tbl[n].get_ni))() == *iter)
{
// If there's a match, check if the rule is include or exclude
const std::string flag = (rule.*(method_tbl[n].get_op))();
// If include, code is included unless a later rule excludes it
if(flag == "INCLUDE"){ included = true; }
// If exclude, code is specifically excluded
else if(flag == "EXCLUDE"){ return false; }
}
}
}
return included;
}
The answer was edited to include only final version.
BTW this problem is fun, just give me some more time and I come up with stl algorithm and functor...
Obviously the code would be much cleaner and simpler, if you could iterate through the ni and op members of Rule in a loop. If you can't refactor Rule, maybe you could create a wrapper around it to achieve this goal.
If you have a single method with such code, I wouldn't bother though. IMO this would only pay off if you can eliminate the duplicated code in several similar methods.
You could get around by creating some kind of tribool class and use lazy evaluation.
class TriState
{
public:
TriState(): mState(KO) {}
bool isValid() const { return mState != FATAL; }
bool ok() const { return mState == OK; }
void update(std::string const& value,
std::string const& reference,
std::string const& action)
{
if (mState == FATAL) return;
if (value == reference)
{
if (action == "INCLUDE") mState = OK;
else if (action == "EXCLUDE") mState = FATAL;
}
}
private:
typedef enum { OK, KO, FATAL } State_t;
State_t mState;
};
Then you can use the loop as such:
TriState state;
for (const_iterator it = nicodes.begin(), end = nicodes.end();
it != end && state.isValid(); ++it)
{
state.update(*it, rule.get_ni1(), rule.get_op1);
state.update(*it, rule.get_ni2(), rule.get_op2);
state.update(*it, rule.get_ni3(), rule.get_op3);
state.update(*it, rule.get_ni4(), rule.get_op4);
state.update(*it, rule.get_ni5(), rule.get_op5);
}
return state.ok();
Now, if the operation on rule have some kind of side effect that should be avoided, you use a wrapper to get lazy evaluation.
class Wrapper
{
public:
Wrapper(Rule const& rule): mRule(rule) {}
std::string const& get_ni(size_t i) const { switch(i) { ... } }
std::string const& get_action(size_t i) const { switch(i) { ... } }
private:
Rule const& mRule;
};
Refactor update:
void update(std::string const& value, Wrapper wrapper, size_t index)
{
if (mState == FATAL) return;
if (value == wrapper.get_ni(index))
{
if (wrapper.get_action(index) == "INCLUDE") mState = OK;
else if (wrapper.get_action(index) == "EXCLUDE") mState = FATAL;
}
}
Use a double loop:
TriState state;
Wrapper wrapper(rule);
for (const_iterator it = nicodes.begin(), end = nicodes.end();
it != end && state.isValid(); ++it)
{
for (size_t index = 1; index != 6 && state.isValid(); ++index)
state.update(*it, wrapper, index);
}
return state.ok();
Guideline: Wrap what you can't modify! (look at the Adaptor family of Patterns)
This is promised algorithms based solution and it is hardcore... And they are saying STL is here to simplify our programs (this is waaaay longer then the other solution I proposed).
struct FUNCTOR : std::unary_function<bool, std::string const &>
{
public:
FUNCTOR(Rule const &r) : included(false), rule(r)
{
}
// returns true if exluded
bool operator()(std::string const &s)
{
static METHODS methods[] =
{
{ &Rule::get_ni1, &Rule::get_op1 },
{ &Rule::get_ni2, &Rule::get_op2 },
{ &Rule::get_ni3, &Rule::get_op3 },
{ &Rule::get_ni4, &Rule::get_op4 },
{ &Rule::get_ni5, &Rule::get_op5 },
};
return(std::find_if(&methods[0], &methods[5], FUNCTOR2(rule, s, included)) != &methods[5]);
}
operator bool()
{
return(included);
}
private:
struct METHODS
{
std::string (Rule::*get_ni)() const;
std::string (Rule::*get_op)() const;
};
struct FUNCTOR2 : std::unary_function<bool, METHODS>
{
public:
FUNCTOR2(Rule const &r, std::string const &s, bool &incl) : rule(r), str(s), included(incl)
{
}
// return true if exluded
bool operator()(METHODS m)
{
if((rule.*m.get_ni)() == str)
{
// If there's a match, check if the rule is include or exclude
const std::string flag = (rule.*m.get_op)();
// If include, code is included unless a later rule excludes it
if(flag == "INCLUDE")
included = true;
// If exclude, code is specifically excluded
else if(flag == "EXCLUDE")
{
included = false;
return(true);
}
}
return(false);
}
private:
Rule const &rule;
std::string const &str;
bool &included;
};
Rule const &rule;
bool included;
};
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
FUNCTOR f(rule);
std::find_if(nicodes.begin(), nicodes.end(), f);
return(f);
}
Using a in/out parameter is a straightforward and efficient way to get two return values:
Plus I presume you need lazy evaluation of rule.get_opN()? To do that you'll need to use a pointer-to-member-function.
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
// Match against the ni codes of the rule
if (!matchNICode(rule.get_ni1(), rule, &Rule::get_op1, included)) return false;
if (!matchNICode(rule.get_ni2(), rule, &Rule::get_op2, included)) return false;
if (!matchNICode(rule.get_ni3(), rule, &Rule::get_op3, included)) return false;
if (!matchNICode(rule.get_ni4(), rule, &Rule::get_op4, included)) return false;
if (!matchNICode(rule.get_ni5(), rule, &Rule::get_op5, included)) return false;
}
return included;
}
bool Translations::matchNICode(const std::string& ni, const Rule& rule,
std::string (Rule::* const opfn)(), bool& included)
{
if (ni == *iter) {
const std::string& op = (rule.*opfn)();
if (op == "INCLUDE") {
included = true;
}
else if (op == "EXCLUDE") {
return false;
}
}
return true;
}