Hi,
While returning something that decomposes to a locked mutex is indeed of questionable direct use from my perspective as well, I think we're missing the obvious (unless any fellow dev already stated):
struct getLockedMutex {
std::mutex m;
getLockedMutex() { m.lock(); }
};
Now, if we can set aside mutex for a while... Indeed, both 'returning char[]' and 'non-movable object' came up this week at work, plus I'm regularly working with handcrafted reflection. While this is not what I'm here for, an object-oriented alloca() - or, if you prefer, annotations - could hit these 3 birds with a stone. What I mean: expr1 @ expr2 could be assigned the following meaning:
- type of expr1 @ expr2 is same as type of expr1
- throwing expr1 @ expr2 is the same as throwing expr1 (expr2 is not evaluated)
- type of @(expr1 @ expr2) is same as expr2 and is allocated (in case of ASDV):
- when directly returning it, i.e., return expr1 @ expr2, in the caller's scope, amongst ASDVs
- returning expr1 @ expr2 requires either the function or lambda specification to contain the annotation (in some form not yet decided at the time of writing)
- evaluation of expr2 in this case is scheduled-before destruction of automatic storage duration variables in the function's scope (thus those variables and function arguments are still available), but scheduled-after evaluation of expr1
- when accessing a member variable, in the scope where the access takes place, amongst ASDVs
- in any other context, in the current scope (but I'm okay with caller's scope as well)
- when expr1 @ expr2, or a function that returns annotated, is directly used as initializer in a definition of an ASDV, member variable, global variable or function-local static variable a_var, (i.e., auto x = expr1 @ expr2; or struct S { int i = 1 @ "name-of-i-in-serialization"; }; ), both the defined variable and the annotation are introduced, in this order.
- evaluation takes place each time when the annotation @a_var is accessed
- for expr1 @ expr2, in expr2, @@ is a reference to expr1 (the evaluation of expr1 is sequenced-before the evaluation of expr2, so this is doable). This allows for calling functions on the non-movable object being returned.
Note that the two names mean that a function call f(expr1 @ expr2) is still the same as f(expr1), but a function returning annotated is to allocate in caller's scope (hence the analogy to alloca) and, for member variables, it's always evaluated when accessed. Thus, it brings us the infamous post-constructor for returning non-movable object, some semi-manual reflection/introspection for serialization-deserialization, the ever-returning 'how to return char[]'; for some realistic use-cases, it'd permit non-movable types with private ctor and named try-constructor (static member function that returns std::optional<T> or std::expected<T, E> because it might or might not fail for the arguments).
Obviously, @ and @@ are simply placeholders for any better syntax. Anyways, this might work for the use-cases on this thread, but I'm rather off to my old proposal, statement expressions, as that could bring some real value to HFT I think...
Bests,
-lorro