Question

I have many different lambdas that all iterate through a list of parameters and pick some of them (with a switch) to do an operation on. I want to make sure that each lambda finds at least one of each case it is looking for and to report back if any case isn't hit. I can setup a vector of bools to track if each case was found, but I need to know the full count of cases in the switch to see if I got them all and to see the indices of the bools. I'd really like to integrate it in the general case macro, that way nothing will break if a case gets added later without updating a count.

Lambda example:

#define OneCase ???

auto MysteryLambda = [](Parameters params) -> int
{
 //something to set the case count to 0, whatever it is

 for (auto param : params)
 {
  switch (param)
  {
   case First:
    OneCase; //case count + 1
    //do operation
    break;
   case Second:
    OneCase; //case count + 1
    //do operation
    break;
   case Third:
    OneCase; //case count + 1
    //do operation
    break;
  }
 }


 static const int cases = ???; //this would be 3

 std::cout << "The enum has " << cases << " cases." << std::endl;

 return cases;
};

Then the lambda can return that const count. Is this possible?

If it isn't possible, is there an alternative way to do this?

Was it helpful?

Solution 3

Idea: Turn a switch into a bunch of if statements that record their presence in an array.

Preamble:

#define BIG_ENOUGH 50     // Just needs to be bigger than the maximum enum value

#define BEGIN_DETECTED_SWITCH(x) \
{ \
    vector<bool> __has(BIG_ENOUGH, false); \
    vector<bool> __saw(BIG_ENOUGH, false); \
    auto __x(x); \
    { ;    // Dummy open brace to be consumed by first DETECTED_CASE()

#define END_DETECTED_SWITCH \
    } \
    for (int i = 0; i < BIG_ENOUGH; ++i) { \
        if (__has[i] && !__saw[i]) { \
            cout << "Didn't see any " << i << "'s!\n"; \
        } \
    } \
}

#define DETECTED_CASE(x) \
    } \
    __has[x] = true; \
    if (__x == (x)) { \
        __saw[x] = true;

Now turn each switch statement into:

BEGIN_DETECTED_SWITCH(someEnumVal)
    DETECTED_CASE(First)
        // Code for first
    DETECTED_CASE(Second)
        // Code for second
    DETECTED_CASE(Third)
        // Code for Third
END_DETECTED_SWITCH

You could slightly generalise it by having END_DETECTED_SWITCH take a lambda parameter that gets called with every non-firing case, instead of hardcoding the "reaction".

OTHER TIPS

It would be just this side of possible to use the GCC/MSVC __COUNTER__ macro for this, though it would entail a significant amount of scaffolding around the switch statement itself. In general, no, it's not possible. It sounds like you want a map of lambdas, not a switch statement.

Ok, Sneftel's __COUNTER__ suggestion is viable! Here's how I managed to make it work:

enum class Parameter //sample parameters, they don't have to be the same for each lambda
{
 First, Second, Third, Fourth
};

typedef std::vector<Parameter> Parameters;

void test()
{
 static const int startcases = __COUNTER__ + 1; //the initial state of counter when this lambda is pre-proced

 auto GetIndex = [](unsigned int counter) -> unsigned int //helper function, it takes __COUNTER__ from wherever it is called
 {
  return counter - startcases;
 };

 //Example lambda.  This isn't built to do anything useful at all.

 auto Example = [&GetIndex](Parameters parameters, std::vector<bool>& used) -> unsigned int
 {
  for (auto parameter : parameters)
  {
   switch (parameter) //only looking at First and Third
   {
   case Parameter::First:
    used[GetIndex(__COUNTER__)] = true;
    break;
   case Parameter::Third:
    used[GetIndex(__COUNTER__)] = true;
    break;
   }
  }

  static const unsigned int cases = __COUNTER__ - startcases; //this would be 2

  std::cout << "The enum has " << cases << " cases." << std::endl;

  return cases;
 };

 //Ok, time for a test!

 //Load out sample params, using VC++2012 so no init list on vectors. :(

 Parameters parameters; 
 parameters.push_back(Parameter::First);
 parameters.push_back(Parameter::Second);
 parameters.push_back(Parameter::First); //no Third in this vector
 parameters.push_back(Parameter::Fourth);

 std::vector<bool> used; //a vector of bools filled to some arbitrary capacity with falses, I can have size checks in a more extensive helper function so no big deal
 used.insert(used.begin(), 10, false);

 unsigned int cases = Example(parameters, used); //returns # of cases in this particular lambda

 for (unsigned int i = 0; i < cases; ++i) //loop through for each case and see if one is false (meaning it had no hit)
 {
  if (!used[i])
  {
   std::cerr << "Didn't process case " << i << "." << std::endl; 
  }
 }
}

Output:

The enum has 2 cases.
Didn't process case 1.

If the number of lines of code per case is fixed (admittedly a hard constraint), the answer is yes !

Insert Begin= __LINE__; before the switch and End= __LINE__; after it. Anytime later, (End - Begin - Extra) / LinesPerCase gives you the answer :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top