-2

I am experimenting with complex FSM frameworks. For one of the approaches I am considering I would like to have two types of function: plain function and special function. The exact meaning does not matter. I want to have compile-time enforcement that there is no more than one special function per block. I can play with function types (e.g. make special actions to return value), signatures, attribute, template meta-programming, wrapper macro, function objects etc.

Each block is very simple just a plain sequence of a few function calls:

{
    funcA();
    funcB();
    ..
    funcC();
}
  • Seems more something for SO... probably after some rewording. – Deduplicator Apr 24 '17 at 12:03
  • 2
    If this was a casino I'd put $20 that what you are asking is impossible from a regular compiler – whatsisname Apr 24 '17 at 15:45
  • It would be best if you created your own tiny language. – Frank Hileman Apr 24 '17 at 21:04
  • please don't... – quant Jun 9 '18 at 5:58
3

I don't think you can do this comfortably. You are trying to implement your own language on top of C++, but C++ doesn't really have this kind of extensibility.

The next best thing is a fluent interface. With a fluent interface, we can encode a grammar of our design into the type system. We can't enforce that only one special function per C++ block is called, but we can enforce that only one special method in our cascade is invoked. For example:

// Mixin for end() operation
struct can_end {
  auto end() && -> void {}
};

// Mixin for normal methods
template<typename next_state>
struct can_normal {
  auto normal_1() && -> next_state { return {}; }
  auto normal_2() && -> next_state { return {}; }
};

// State after special method: only end and normal methods allowed
struct after_special : can_end, can_normal<after_special> {
  after_special() = default;
  after_special(after_special const&) = delete;
};

// State before special method: end, normal, and special allowed
struct before_special : can_end, can_normal<before_special> {
  before_special() = default;
  before_special(before_special const&) = delete;

  auto special() && -> after_special { return {}; }
};

// Entry point
auto block() -> before_special { return {}; }

Now we can write a method cascade like:

block()
  .normal_1()
  .special()
  .normal_2()
  .end();

But will be prevented from invoking the special method twice:

block()
  .special()
  .special()  // type error
  .end();

By deleting the copy ctor, we prevent using a temporary variable to capture the pre-special state:

auto temp = block();  // compilation error: deleted copy ctor
temp.special();
temp.special().end();

As all methods take an rvalue reference, this also discourages trickery like binding the state to a reference and then invoking the special method twice. But I'm sure the C++ type system has enough escape hatches to do that if you're really determined.

  • It seems like a great idea. – zzz777 Apr 25 '17 at 15:06
  • Caution: the deleted copy constructor will stop working in C++17 because of guaranteed copy elision. I don't know of a workaround though :/ – Quentin May 4 '17 at 17:59
3

Maybe I've overlooked something, but given your strict requirements on blocks the following should be enough:

#define specialFunction(...) \
    int SPECIAL_FUNCTION_ALREADY_CALLED = 0; \
    specialFunction(__VA_ARGS__)

Trying to call specialFunction twice in the same block will redefine the variable, thus triggering a compile-time error.

main.cpp:6:9: error: redeclaration of 'int SPECIAL_FUNCTION_ALREADY_CALLED'
     int SPECIAL_FUNCTION_ALREADY_CALLED = 0; \
         ^

Caveat: does not work across nested blocks, breaks all uses of the function call as an expression. But it does seem to fit the bill.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.