Have enum value be equivalent to many others - c++

I need to use an enum of various values, in this case various building pieces. Most of these are unique, but there a few that I'd like to be equivalent. I mean as follows:
enum class EPiece: uint8 {
Ceiling,
Table,
Door,
WestWall,
NorthWall,
SouthWall,
EastWall,
Wall,
Floor
};
And I'd like to Wall == WestWall to be true, as well as Wall == NorthWall, etc. However, WestWall == NorthWall is false.
Why I am doing this is because I am making a game where various pieces have a definition based off of what they are/where they are. The player has to place various pieces in a predefined order. The player first has to place a NorthWall piece. They will have available various pieces, and will have to select a Wall piece, and have to attempt to place it on a NorthWall piece. The game checks if the two are equivalent (in this case true), and if the current piece to place is NorthWall. If they attempt to place it on a WestWall piece it should fail since it's not that stage yet.
I thought of doing this through flags, doing something like
WestWall = 0x01,
NorthWall = 0x02,
SouthWall = 0x04,
EastWall = 0x08,
Wall = WestWall | NorthWall | SouthWall | EastWall
and checking by doing something like:
// SelectedPiece is the Piece the Player selected and is attempting to place
// PlacedOnPiece is the Piece that we are attempting to place on top of
// CurrentPieceToPlace is what Piece we are supposed to place at this stage
if ((CurrentPieceToPlace == PlacedOnPiece) && (SelectedPiece & PlacedOnPiece != 0)) {
}
The thing is, I have a lot of pieces and my understanding is to make the flags work I have to use powers of two. That means if I use uint32 I could have a max of 32 Pieces, and I don't want to be limited by that. I might only need around 20, but I don't want to get stuck.
Any suggestions? At this point I need to use an enum, so I can't try a different type.

I'd advise against overloading == to have that meaning. == is usually transitive (if A==B and B==C, then A==C), and if it fails to be transitive otherwise "sane" code will break.
Start with your enum:
enum class EPiece: uint8 {
Ceiling,
Table,
Door,
WestWall,
NorthWall,
SouthWall,
EastWall,
Wall,
Floor
};
Now define an can_be_used_as_a relationship.
bool can_be_used_as_a( EPiece x, EPiece used_as_a_y ) {
if (x==y) return true;
switch(x) {
case Wall: {
switch(used_as_a_y) {
case WestWall:
case EastWall:
case NorthWall:
case EastWall:
return true;
default: break;
}
}
default: break;
}
switch(used_as_a_y) {
case Wall: {
switch(x) {
case WestWall:
case EastWall:
case NorthWall:
case EastWall:
return true;
default: break;
}
}
default: break;
}
return false;
}
now can_be_used_as_a( WestWall, Wall ) is true because a WestWall can be used as a Wall. And similarly, Wall can be used as a WestWall. But a WestWall cannot be used as a EastWall.
If you want slightly cleaner syntax, we can write a named operator:
namespace named_operator {
template<class D>struct make_operator{make_operator(){}};
template<class T, char, class O> struct half_apply { T&& lhs; };
template<class Lhs, class Op>
half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}
template<class Lhs, class Op, class Rhs>
auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
-> decltype( invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}
for the 12 line named operator library, used like:
struct used_as_a_tag{};
static const named_operator::make_operator<used_as_a_tag> can_use_as_a;
bool invoke( EPiece x, used_as_a_tag, EPiece y ) {
return can_be_used_as_a(x,y);
}
and now we can do this:
if (x *can_use_as_a* y) {
}
with the operator occurring between the left and right operands. But this might be going too far.
Finally, consider using enum class instead of enum.

You're going in the right direction. Each wall type you have represents a single bit, and that's awesome. Now all you have to do is to combine them in Wall, and to extract them in your checks, so:
WestWall = 0x01, //0b0001
NorthWall = 0x02, //0b0010
SouthWall = 0x04, //0b0100
EastWall = 0x08, //0b1000
Wall = 0xF //0b1111
Now, to check if one value of the enum represents an other value, you should write something like this:
bool isSame(EPiece first, EPiece second)
{
//if they are the same, they are, well... the same.
if(first == second)
return true;
//this only leaves the bits that are present in both values, so
//if the result is different from 0, then second is a part of first, so
//we return true
else if(first & second)
return true;
//if we are here, then first and second are unrelated
return false;
}

You can define your own comparison operators, like this:
bool operator==(EPiece lhs, EPiece rhs)
{
if (int(lhs) == int(EPiece::Wall) &&
(int(rhs) == int(EPiece::NorthWall) ||
int(rhs) == int(EPiece::SouthWall))) // lots more cases...
{
return true;
}
return int(lhs) == int(rhs);
}
Do note that the declaration (though not necessarily the definition) of the above must be visible wherever you expect to compare these things, so you should declare it right alongside the enum declaration.

Here are two slightly different possibilites:
enum {
Flag0 = 1 << 0,
Flag1 = 1 << 1,
Flag2 = 1 << 2,
Flag3 = 1 << 3,
FlagMask = 0x07
}
if (value & FlagMask) // it's got some flags
{ ... }
if (value & Flag3) // Flag3
{ ... }
and
enum {
ItemA0,
ItemABegin = ItemA0,
ItemA1,
ItemA2,
// insert ItemAs here
ItemAEnd,
ItemB0,
ItemBBegin = ItemB0,
ItemB1,
// insert ItemBs here
ItemBEnd,
}
if (ItemABegin <= value && value < ItemAEnd) // it's some ItemA
{ ... }
if (ItemBBegin <= value && value < ItemBEnd) // it's some ItemB
{ ... }
switch (value) { // switch on specific types
case ItemB0: ... break;
case ItemB1: ... break;
}
the second version still encapsulates the idea of an enumeration type.

Related

Function overloaded by bool and enum type is not differentiated while called using multiple ternary operator in C++

Got into an interesting problem while tried to call the overloaded function using conditional operator (just to avoid multiple if else condition)
class VirtualGpio
{
typedef enum
{
OUTPUT = 0xC7,
INPUT ,
DIRINVALID
}GpioDirection;
struct pinconfig
{
struct pinmap pin;
GpioPolarity plrty;
bool IsPullupCfgValid;
bool IsTriStCfgValid;
bool IsInputFilterValid;
GpioDirection dic;
gpiolistner fptr; // Callback function pointer on event change
};
};
class factory
{
public:
VirtualGpio *GetGpiofactory(VirtualGpio::pinconfig *cfg,VirtualGpio::GpioAccessTyp acc=VirtualGpio::Pin);
private:
int setCfgSetting(VirtualGpio::pinmap * const getpin, VirtualGpio::GpioDirection const data);
int setCfgSetting(VirtualGpio::pinmap * const getpin, bool const data);
};
int factory::setCfgSetting(VirtualGpio::pinmap * const getpin, VirtualGpio::GpioDirection const data)
{
cout << "It is a Direction overloaded" << endl;
}
int factory::setCfgSetting(VirtualGpio::pinmap * const getpin, bool const data)
{
cout << "It is a bool overloaded" << endl;
}
VirtualGpio* factory::GetGpiofactory(VirtualGpio::pinconfig *cfg,VirtualGpio::GpioAccessTyp acc)
{
VirtualGpio * io = new VirtualGpio();
printf("acc : 0x%X, pin : 0x%x, port : 0x%x\n",acc, cfg->pin.pinno, cfg->pin.portno);
printf("value of expression : 0x%x\n",((acc == VirtualGpio::Pin)? cfg->dic : ((cfg->dic == VirtualGpio::INPUT)?true :false))); <= this prints the right value
if(acc == VirtualGpio::Pin)
setCfgSetting(&cfg->pin,cfg->dic);
else if(cfg->dic == VirtualGpio::INPUT)
setCfgSetting(&cfg->pin,true);
else
setCfgSetting(&cfg->pin,false);
#if 0
if(setCfgSetting(&cfg->pin, ((acc == VirtualGpio::Pin)? cfg->dic : ((cfg->dic == VirtualGpio::INPUT)?true :false))) == ERROR)
{
printf("Error Setting the IO configuration for XRA\n");
}
else
printf("Set IO config successfully\n");
#endif
return io;
}
The commented part #if 0 in GetGpiofactory() is same as the above
multiple if-else-if-else block, but if I uncomment the #if0 part to #if
1, for all the possible inputs only bool version of the overloaded
function i.e setCfgSetting(VirtualGpio::pinmap * const getpin, bool
const data) is invoked.
below is my main code.
main()
{
static struct VirtualGpio::pinconfig cfg = {
.pin = {
.location = VirtualGpio::GPIO_ON_GPIOEXP1_TCI,
.pinno = 0,
.portno = -1
},
.plrty = VirtualGpio::active_high,
.IsPullupCfgValid = true,
.IsTriStCfgValid = true,
.IsInputFilterValid = true,
.dic = VirtualGpio::OUTPUT,
.fptr = NULL
};
factory fac;
fac.GetGpiofactory(&cfg);
}
Surprised, the overloaded function works well if I don't use the ternary operator instead use multiple if-else if-else blocks. curious to understand the reason.
That is because the ternary operator always evaluates to a single type. You can't "return" different types with this operator.
When the compiler encounters such an expression he tries to figure out whether he can reduce the whole thing to one type. If that's not possible you get a compile error.
In your case there is a valid option using bool as a type. Because cfg->dic is an enum type which is implicitly convertible to bool. If you would use and enum class your code would not compile anymore showing you what your actual problem is (example).
Also I don't really see what the advantage of this kind of code is. In my opinion it makes the code much harder to read. You could reduce your ifs to just one, if you're concerned about too many of them:
if(acc == VirtualGpio::Pin)
setCfgSetting(&cfg->pin,cfg->dic);
else
setCfgSetting(&cfg->pin, cfg->dic == VirtualGpio::INPUT);

how to sort a std::set using more than one key

I need to copy a set to another one based on more than one key.
the keys are used to -collectively- maintain the uniqueness as well as the order of elements in the set.
My class:
class LaneConnector {
public:
const Lane* getLaneFrom() const {
return From;
}
const Lane* getLaneTo() const {
return To;
}
private:
Lane* From;
Lane* To;
}
my functor:
struct MyLaneConectorSorter {
bool operator() (const LaneConnector* rhs, const LaneConnector* lhs) const
{
const Lane* a = lhs->getLaneFrom();
const Lane* b = rhs->getLaneFrom();
bool key1 = a->getLaneID() < b->getLaneID();
bool key2 = a->getLaneParent->ID() < b->getLaneParent->ID();
bool key2 = a->getLaneParent->getParent->ID() < b->getLaneParent->getParent->ID();
//remind you that I NEED the elements to be in ascending order of
//getLaneParent->getParent->ID() ,a->getLaneParent->ID() and then a->getLaneID()
//duplicate elements are the ones which have all three keys same and need to be discarded
return (key1 && key2 && key3); //which dont seem to be working
}
};
and my source and origin sets:
const std::set<LaneConnector*> src = ..... ; //the getter give me a const version
std::set<sim_mob::LaneConnector *, MyLaneConectorSorter> dest;
and how I fill it up:
for(std::set<sim_mob::LaneConnector*>::iterator it = tempLC.begin(); it != tempLC.end(); it++)
{
dest.insert(*it);//I know I can insert it right at the time of declaration, but keep it like this for now...please
}
your kind help would be highly appreciated.
Since getting operator< for multiple tests right is rather hard, I advocate my way of doing this with tuple (in this case with make_tuple instead of tie since we're dealing with temporaries returned from functions):
#include <tuple>
struct MyLaneConectorSorter {
bool operator() (const LaneConnector* lhs, const LaneConnector* rhs) const
{
const Lane* a = lhs->getLaneFrom();
const Lane* b = rhs->getLaneFrom();
auto const* pa = a->getLaneParent();
auto const* pb = b->getLaneParent();
return std::make_tuple(a->getLaneID(), pa->ID(), pa->getParent()->ID()) <
std::make_tuple(b->getLaneID(), pb->ID(), pb->getParent()->ID())
}
This should work and you can get tuple and make_tuple from Boost too, if your compiler doesn't offer them yet.
You need to prioritorise your key field comparisons... only if the most important field is equal, then you compare the second most important - if that's equal then you compare the third most important etc.. As soon as there's an inequality, you return true or false as appropriate. So, it's not a && operation, it should be ? : or an if-else chain, as in:
return lhs.key1 < rhs.key1 ? true :
rhs.key1 < lhs.key1 ? false :
lhs.key2 < rhs.key2 ? true :
rhs.key2 < lhs.key2 ? false :
...
false;
For the set to operate correctly, you must ensure the keys are never equal - so that last false is never actually used.
If you have three member foo, bar and baz to compare on, this is a common way to compare them:
return lhs.foo < rhs.foo
|| lhs.foo == rhs.foo && (lhs.bar < rhs.bar
|| lhs.bar == rhs.bar && lhs.baz < rhs.baz);
Do you see the pattern? ;)
I have problem understanding your sorting rules, but if the relation is a simple sub-sort than the code should look like this:
if (a->getLaneID() < b->getLaneID())
return true;
else if (a->getLaneID() == b->getLaneID())
{
if (a->getLaneParent->ID() < b->getLaneParent->ID())
return true;
// etc...
}
return false;
Your class MyLaneConnectionSorter has a flaw.
std::set expects a comparison class that can order elements. So your comparison function must provide behaviour similar to less functor or operator<, i.e. either a < b or a > b (which is b < a) or a == b (which is !(a < b) && !(a > b))
If we take your comparison function, it will consider Lanes (6, 5, 4) and (7, 3, 4) (in format (PPID, PID, ID)) to be equal, because neither one is less than another. So you need to compare like this:
if (a->getLaneParent->getParent->ID() < b->getLaneParent->getParent->ID()) return true;
else if (a->getLaneParent->getParent->ID() > b->getLaneParent->getParent->ID()) return false;
else {
if (a->getLaneParent->ID() < b->getLaneParent->ID()) return true;
else if (a->getLaneParent->ID() > b->getLaneParent->ID()) return false;
else {
return (a->getLaneID() < b->getLaneID());
}
}

Can C++ do something like an ML case expression?

So, I've run into this sort of thing a few times in C++ where I'd really like to write something like
case (a,b,c,d) of
(true, true, _, _ ) => expr
| (false, true, _, false) => expr
| ...
But in C++, I invariably end up with something like this:
bool c11 = color1.count(e.first)>0;
bool c21 = color2.count(e.first)>0;
bool c12 = color1.count(e.second)>0;
bool c22 = color2.count(e.second)>0;
// no vertex in this edge is colored
// requeue
if( !(c11||c21||c12||c22) )
{
edges.push(e);
}
// endpoints already same color
// failure condition
else if( (c11&&c12)||(c21&&c22) )
{
results.push_back("NOT BICOLORABLE.");
return true;
}
// nothing to do: nodes are already
// colored and different from one another
else if( (c11&&c22)||(c21&&c12) )
{
}
// first is c1, second is not set
else if( c11 && !(c12||c22) )
{
color2.insert( e.second );
}
// first is c2, second is not set
else if( c21 && !(c12||c22) )
{
color1.insert( e.second );
}
// first is not set, second is c1
else if( !(c11||c21) && c12 )
{
color2.insert( e.first );
}
// first is not set, second is c2
else if( !(c11||c21) && c22 )
{
color1.insert( e.first );
}
else
{
std::cout << "Something went wrong.\n";
}
I'm wondering if there's any way to clean all of those if's and else's up, as it seems especially error prone. It would be even better if it were possible to get the compiler complain like SML does when a case expression (or statement in C++) isn't exhaustive. I realize this question is a bit vague. Maybe, in sum, how would one represent an exhaustive truth table with an arbitrary number of variables in C++ succinctly? Thanks in advance.
I like Alan's solution but I respectfully disagree with his conclusion that it is too complex. If you have access to C++11 it gives you almost all the tools you need. You only need to write one class and two functions:
namespace always {
struct always_eq_t {
};
template <class lhs_t>
bool operator==(lhs_t const&, always_eq_t)
{
return true;
}
template <class rhs_t>
bool operator==(always_eq_t, rhs_t const&)
{
return true;
}
} // always
Then you can write your function in a way relatively similar to ML:
#include <tuple>
#include <iostream>
void f(bool a, bool b, bool c, bool d)
{
always::always_eq_t _;
auto abcd = std::make_tuple(a, b, c, d);
if (abcd == std::make_tuple(true, true, _, _)) {
std::cout << "true, true, _, _\n";
} else if (abcd == std::make_tuple(false, true, _, false)) {
std::cout << "false, true, _, false\n";
} else {
std::cout << "else\n";
}
}
int
main()
{
f(true, true, true, true);
f(false, true, true, false);
return 0;
}
In C++ you often want to consider is there a sensible type that I can create that will help me write my code more easily? Additionally, I think if you have a background in ML you will benefit a lot from examining C++ templates. They are very helpful in applying a functional programming style in C++.
C++ is traditionally oriented to the individual, and you could never do anything resembling the following regardless of syntax.
if ([a,b,c,d] == [true,true,false, false]) {}
The New C++ standard has some stuff that lets you define arrays of constants inline, and so it is possible to define a class that will take in an array as a constructor and support such comparisons. Something like
auto x = multi_val({a,b,c,d});
if (x == multi_val({true, true, false, false}))
{ ... }
else if (x == multi_val(etc.))
But now to do partial matches like with the _, that's not directly supported and you'd have to make your class even more complex to fudge with that, like using a maybe template type and going
multi_val(true, true, maybe<bool>(), maybe<bool>)
This gets into rather heady C++ territory and definitely not what I would do for something so elementary.
For C++11 assuming that you only want to match a fixed number of booleans and can live without the _ pattern matching then [1] (Expand to the number of variables you require).
I'm still working on an alternate solution using templates to match arbitrary types using lambdas or functors for the expressions.
-Edit-
As promised, [2] pattern matching of arbitrary types incl. unspecified values.
Note a couple of caveats:
This code only works with 4 variables (actually my first foray into template metaprogramming). This could very much be improved with variadic templates.
It works but it's not very tidy or well organised. More a proof of concept that would need to be cleaned up before introducing into production code.
I'm not happy with the match function. I was hoping to use initializer lists to pass the expressions to be evaluated and stop on the first match (with the current implementation every matching condition will be executed) - however i couldn't quickly think of how to pass expression matching objects of different types via the single initializer list.
I can't think of a method for either to validate that the truth table is exhaustive.
Cheers,
-nick
[1]
constexpr int match(bool v, int c)
{
return v ? (1 << c) : 0;
}
constexpr int match(bool a, bool b)
{
return match(a, 0) | match(b, 1);
}
int main()
{
int a = true;
int b = false;
switch(match(a, b))
{
case match(false, false):
break;
case match(false, true):
break;
case match(true, false):
break;
case match(true, true):
break;
}
}
[2]
template<typename V1, typename V2, typename V3, typename V4>
class pattern_match_t
{
private:
V1 value_0;
V2 value_1;
V3 value_2;
V4 value_3;
public:
typedef std::function<void(V1, V2, V3, V4)> expr_fn;
template <typename C1, typename C2, typename C3, typename C4>
pattern_match_t<V1, V2, V3, V4>& match(C1 a, C2 b, C3 c, C4 d, expr_fn fn)
{
if(value_0 == a && value_1 == b && value_2 == c && value_3 == d)
fn(value_0, value_1, value_2, value_3);
return *this;
}
pattern_match_t(V1 a, V2 b, V3 c, V4 d)
: value_0(a), value_1(b), value_2(c), value_3(d)
{
}
};
template<typename T>
class unspecified
{};
template<typename T>
constexpr bool operator==(unspecified<T>, const T&)
{
return true;
}
template<typename T>
constexpr bool operator==(const T&, unspecified<T>)
{
return true;
}
template<typename V1, typename V2, typename V3, typename V4>
pattern_match_t<V1, V2, V3, V4> pattern_match(V1 a, V2 b, V3 c, V4 d)
{
return pattern_match_t<V1, V2, V3, V4>(a, b, c, d);
}
int main()
{
bool test_a = true;
std::string test_b = "some value";
bool test_c = false;
bool test_d = true;
pattern_match(test_a, test_b, test_c, test_d)
.match(true, unspecified<std::string>(), false, true, [](bool, std::string, bool, bool)
{
return;
})
.match(true, "some value", false, true, [](bool, std::string, bool, bool)
{
return;
});
}

branching based on two boolean variables

Suppose I have two boolean variables, and I want to do completely different things based on their values. What is the cleanest way to achieve this?
Variant 1:
if (a && b)
{
// ...
}
else if (a && !b)
{
// ...
}
else if (!a && b)
{
// ...
}
else
{
// ...
}
Variant 2:
if (a)
{
if (b)
{
// ...
}
else
{
// ...
}
}
else
{
if (b)
{
// ...
}
else
{
// ...
}
}
Variant 3:
switch (a << 1 | b)
{
case 0:
// ...
break;
case 1:
// ...
break;
case 2:
// ...
break;
case 3:
// ...
break;
}
Variant 4:
lut[a][b]();
void (*lut[2][2])() = {false_false, false_true, true_false, true_true};
void false_false()
{
// ...
}
void false_true()
{
// ...
}
void true_false()
{
// ...
}
void true_true()
{
// ...
}
Are variants 3 and 4 too tricky/complicated for the average programmer? Any other variants I have missed?
The first variant is the clearest and most readable, but it can be adjusted:
if (a && b) {
// ...
} else if (a) { // no need to test !b here - b==true would be the first case
// ...
} else if (b) { //no need to test !a here - that would be the first case
// ...
} else { // !a&&!b - the last remaining
// ...
}
You forgot about:
if (a) a_true(b);
else a_false(b);
which is probably the best choice when appliable, and when you truly need 4 different behaviours.
If you have more than 2 bools, I take this as a code smell if I have 2^n different behaviours which don't factorize well like the above. Then I may think about doing:
enum { case1, case2, ... }
int dispatch_cases(bool a, bool b, bool c, ..., bool z);
switch (dispatch_cases(a, b, ..., z))
{
case case1:
...
};
but without context, it is hard to tell whether such complexity is necessary.
IMHO, I will go for variant 3. Because personally, I don't like if/else when I am checking for equality. It clearly states that there are only 4 possibilities.
One minor edit would be:
inline int STATES(int X, int Y) { return (X<<1) | Y; }
// ...
switch (STATES(a,b))
To make it more fancy, you may replace 0,1,2,3 with an enum as well.
enum States {
NONE,
ONLY_B.
ONLY_A,
BOTH
};
For just two booleans, any of them is good and reasonable. One can choose based on his taste.
However, if there are more than two booleans, say four booleans, then I personally would go with lookup table, and I would do this as:
typedef void (*functype)();
//16 functions to handle 16 cases!
void f0() {}
void f1() {}
//...so on
void f15() {}
//setup lookup table
functype lut[] =
{
f0, //0000 - means all bool are false
f1, //0001
f2, //0010
f3, //0011
f4, //0100
f5, //0101
f6, //0110
f7, //0111
f8, //1000
f9, //1001
f10, //1010
f11, //1011
f12, //1100
f13, //1101
f14, //1110
f15 //1111 - means all bool are true
};
lut[MakeInt(b1,b2,b3,b4)](); //call
MakeInt() is easy to write:
int MakeInt(bool b1, bool b2, bool b3, bool b4)
{
return b1 | (b2<<1) | (b3 <<2) | (b4<<3);
}

Is there a better way? While loops and continues

There are many functions within the code I am maintaining which have what could be described as boilerplate heavy. Here is the boilerplate pattern which is repeated ad nausea throughout the application when handling DB I/O with a cursor:
if( !RowValue( row, m_InferredTable->YearColumn(), m_InferredTable->YearName(), m_InferredTable->TableName(), value )
|| !IsValidValue( value ) )
{
GetNextRow( cursor, m_InferredTable );
continue;
}
else
{
value.ChangeType(VT_INT);
element.SetYear( value.intVal );
}
The thing is not all of these statements like this deal with ints, this "element" object, the "year" column, etc. I've been asked to look at condensing it even further than it already is and I can't think of a way to do it. I keep tripping over the continue statement and the accessors of the various classes.
Edit: Thanks to all those that commented. This is why I love this site. Here is an expanded view:
while( row != NULL )
{
Element element;
value.ClearToZero();
if( !GetRowValue( row, m_InferredTable->DayColumn(), m_InferredTable->DayName(), m_InferredTable->TableName(), value )
|| !IsValidValue( value ) )
{
GetNextRow( cursor, m_InferredTable );
continue;
}
else
{
value.ChangeType(VT_INT);
element.SetDay( value.intVal );
}
And things continue onward like this. Not all values taken from a "row" are ints. The last clause in the while loop is "GetNextRow."
Okay, from what you've said, you have a structure something like this:
while (row!=NULL) {
if (!x) {
GetNextRow();
continue;
}
else {
SetType(someType);
SetValue(someValue);
}
if (!y) {
GetNextRow();
continue;
}
else {
SetType(SomeOtherType);
SetValue(someOtherValue);
}
// ...
GetNextRow();
}
If that really is correct, I'd get rid of all the GetNextRow calls except for the last one. I'd then structure the code something like:
while (row != NULL) {
if (x) {
SetType(someType);
SetValue(someValue);
}
else if (y) {
SetType(someOtherType);
SetValue(SomeOtherValue);
}
// ...
GetNextRow();
}
Edit: Another possibility would be to write your code as a for loop:
for (;row!=NULL;GetNextRow()) {
if (!x)
continue;
SetTypeAndValue();
if (!y)
continue;
SetTypeandValue();
// ...
Since the call to GetNextRow is now part of the loop itself, we don't have to (explicitly) call it each time -- the loop itself will take care of that. The next step (if you have enough of these to make it worthwhile) would be to work on shortening the code to set the types and values. One possibility would be to use template specialization:
// We never use the base template -- it just throws to indicate a problem.
template <class T>
SetValue(T const &value) {
throw(something);
}
// Then we provide a template specialization for each type we really use:
template <>
SetValue<int>(int value) {
SetType(VT_INT);
SetValue(value);
}
template <>
SetValue<float>(float value) {
SetType(VT_FLOAT);
SetValue(value);
}
This lets you combine a pair of calls to set the type and the value into a single call.
Edit: As far as cutting processing short goes, it depends -- if parsing a column is expensive (enough to care about) you can simply nest your conditions:
if (x) {
SetTypeAndValue();
if (y) {
SetTypeAndValue();
if (z) {
SetTypeAndValue();
and so on. The major shortcoming of this is that it'll get pretty deeply nested if (as you've said) you have 20+ conditions in a single loop. That being the case, I'd probably think hard about the for-loop based version I gave above.
Why not make a function to do all the work?
bool processElement(Element& element, Row* row, int value, Table& m_InferredTable, /*other params*/)
{
if( !GetRowValue( row, m_InferredTable->DayColumn(), m_InferredTable->DayName(), m_InferredTable->TableName(), value )
|| !IsValidValue( value ) )
{
GetNextRow( cursor, m_InferredTable );
return true;
}
else
{
value.ChangeType(VT_INT);
element.SetDay( value.intVal );
}
return false;
}
In your loop
while (row != NULL)
{
if (processElement(element, row, value, m_InferredTable))
continue;
// other code
}
Why not invert your if-test?
if (RowValue(row, m_InferredTable->YearColumn(), m_InferredTable->YearName(), m_InferredTable->TableName(), value )
&& IsValidValue( value ))
{
value.ChangeType(VT_INT);
element.SetYear( value.intVal );
}
else
{
GetNextRow( cursor, m_InferredTable );
}
My instinctual approach is to build a polymorphic approach here, where you eventually wind up doing something like(modulo your language and exact logic):
db_cursor cursor;
while(cursor.valid())
{
if(cursor.data.valid())
{
process();
}
cursor.next();
}
db_cursor would be a base class that your different table type classes inherit from, and the child classes would implement the validity functions.
Move it into a template function, templated on the element type (e.g. integer), which you can call over and over. Vary the behavior per data type with a trait template.
template <typename T> struct ElemTrait<T> {};
template <> struct ElemTrait<int> {
static inline void set(Val &value, Elem &element) {
value.ChangeType(VT_INT);
element.SetYear(value.intVal);
}
};
// template <> struct ElemTrait<float> { ... };
template <typename T>
void do_stuff( ... ) {
// ...
if (!RowValue(row,
m_InferredTable->YearColumn(),
m_InferredTable->YearName(),
m_InferredTable->TableName(), value)
|| !IsValidValue(value)
) {
GetNextRow(cursor, m_InferredTable);
continue;
} else {
ElemTrait<T>::set(value, element);
}
// ...
}
You can take out all the GetNextRow calls and the else clauses:
for (row = GetFirstRow () ; row != null ; GetNextRow ())
{
Element element;
value.ClearToZero();
if( !GetRowValue( row, m_InferredTable->DayColumn(), m_MetInferredOutTable->DayName(), m_MetInferredOutTable->TableName(), value )
|| !IsValidValue( value ) )
{
continue;
}
value.ChangeType(VT_INT);
element.SetDay( value.intVal );
}