1

The context

I am trying to implement an easy to use async/await pattern in my C++ code, and I have a way of doing it that I find remarkably simple to use. However, since I am using macros, it also comes at a cost in terms of weakness against exotic code.

Here is the idea:

  • We wrap the execution context in a class, whose members are the variables used by the async function.
  • Inside the body of the async function I implement with macros a switch on a member entrypoint that I use to keep track where jump to when the execution of the function is resumed:

    switch(this->entrypoint)
    {
        // All the async code occurs here, like:
        await(this->foo = bar());
        std :: cout << this->foo << std :: endl;
        await(this->foo2 = bar2());
        std :: cout << this->foo2 << std :: endl;
    }
    
  • The await(assignment) macro actually defines something in the form:

    this->entrypoint = (some value x);
    return;
    case x:
    

Now, this is obviously less compact than a full-fledged async function we could have, for example, in Javascript, but it is more than enough for my needs. Also this prevents nested switches (which I use very rarely) and you cannot define variables locally unless you wrap them somehow in a { /*..*/ }. But I get a super compact way of doing asynchronous operations with a synchronous style.

Remark: The downsides and weaknesses of this strategy are quite clear to me, this is not the point of my question, I just added this to help understand what I need.

The question

The above compiles and runs nicely. However, I am having a serious problem: in the await macro, I need to generate some unique value x to use in the case statement. So far, I am doing it using __LINE__, which works nicely and prevents collisions for most practical purposes. What happens, however, when you write two await statements on the same line?

So I wonder: is there a better way than using __LINE__ to uniquely identify a call to a macro, so that whenever I call the same macro from two distinct places in my code I get different values expanded?

Matteo Monti
  • 8,362
  • 19
  • 68
  • 114

1 Answers1

3

Template instantiation (Unstable)

The type system offers methods of creating compile time variables; however, it does not offer any method of maintaining state between calls.

But, using the method described here you can create a constexpr counter in the templating system by checking which functions have been created, which may work for you; however, this system depends on some promises from the way gcc generates functions and may not work in the future, it currently fails on clang.

Because everything here operates at compile time (mostly in the type system) you get both the ability to use the values in switch statements and (nearly) zero overhead at runtime.


Macros Making Functions (Global Scope)

This post describes a method of making a compile time counter entirely out of macros; however, to make the values constexpr it creates new functions and can only increment fromm the outside of a function.


__COUNTER__ Extension

The pre-processor's standard offers no method of requesting a unique value; however, as mentioned in comments GCC has an extension for __COUNTER__ that will perform the requested task. This is also supported by Visual Studio and Clang.


Not Using Switch

A simple fix (although i don't know enough about your implementation) might be to remove the switch statement in favour of chained if else-if sets.

#define aswitch(x) \
    int position_counter=0;
    if(this->entrypoint==position_counter++)
#define await(x) \
    this->entrypoint=position_counter; \
    }else if(this->entrypoint==position_counter++){
aswitch(this->entrypoint)
{
    await(bar());
    std :: cout << this->foo << std :: endl;
    await(bar2());
    std :: cout << this->foo2 << std :: endl;
}
Community
  • 1
  • 1
unDeadHerbs
  • 1,306
  • 1
  • 11
  • 19