Date: Tue, 11 Apr 2023 14:16:54 +0100
Today I wrote the following function for a microcontroller:
bool Serial(char *const p)
{
# define BAIL { strcpy(p, "0000000000000000"); return false; }
int32_t p32[2u];
if ( (SUCCESS != fpgaReadInt32(0x00f2, &p32[0])) || (0 ==
(p32[0] & 1)) ) BAIL
if ( SUCCESS != fpgaReadInt32(0x00f6, &p32[0]) ) BAIL
if ( SUCCESS != fpgaReadInt32(0x00fa, &p32[1]) ) BAIL
sprintf(p + 0u, "%08lx", static_cast<long unsigned>(p32[0]));
sprintf(p + 16u, "%08lx", static_cast<long unsigned>(p32[1]));
return true;
# undef BAIL
}
I would have liked to use a lambda instead of a macro there, but then
I would have to write:
bool Serial(char *const p)
{
auto BAIL = [p](void)->void { strcpy(p, "0000000000000000"); };
int32_t p32[2u];
if ( (SUCCESS != fpgaReadInt32(0x00f2, &p32[0])) || (0 ==
(p32[0] & 1)) ) { BAIL(); return false; }
if ( SUCCESS != fpgaReadInt32(0x00f6, &p32[0]) ) { BAIL();
return false; }
if ( SUCCESS != fpgaReadInt32(0x00fa, &p32[1]) ) { BAIL();
return false; }
sprintf(p + 0u, "%08lx", static_cast<long unsigned>(p32[0]));
sprintf(p + 16u, "%08lx", static_cast<long unsigned>(p32[1]));
return true;
}
So I thinking, what if we had a 'long return lamda'? The return
statement from a 'long return lambda' is treated as a return statement
from the enclosing function. As follows:
bool Serial(char *const p)
{
auto BAIL = [p](void) -> long return bool { strcpy(p,
"0000000000000000"); return false; };
int32_t p32[2u];
if ( (SUCCESS != fpgaReadInt32(0x00f2, &p32[0])) || (0 ==
(p32[0] & 1)) ) BAIL();
if ( SUCCESS != fpgaReadInt32(0x00f6, &p32[0]) ) BAIL();
if ( SUCCESS != fpgaReadInt32(0x00fa, &p32[1]) ) BAIL();
sprintf(p + 0u, "%08lx", static_cast<long unsigned>(p32[0]));
sprintf(p + 16u, "%08lx", static_cast<long unsigned>(p32[1]));
return true;
}
Of course, you wouldn't be able to store one of these lambdas inside a
normal 'std::function', you'd need to store it inside a
'std::long_return_function' instead.
A few important points:
(1) The return type of a 'long return lambda', if not omitted, must be
implicitly-convertible to the return type of the enclosing function.
(2) You cannot analyse the return value from a 'long return lambda'
inside the enclosing function. For example the initialisation of a
variable inside the enclosing function cannot involve the invocation
of a 'long return lambda', so the following isn't valid:
int MyFunc(void)
{
auto monkey = []()->long return { return 5; }
int i = monkey(); // ill-formed - compiler must issue diagonstic
}
(3) However you can analyse the return value higher up the function
call hierarchy:
int Func2(void)
{
auto Bail = []() -> long return int { return 5; }
// int i = Bail(); - This would be invalid
Bail();
}
int Func(void)
{
int i = Func2(); // This is valid
}
bool Serial(char *const p)
{
# define BAIL { strcpy(p, "0000000000000000"); return false; }
int32_t p32[2u];
if ( (SUCCESS != fpgaReadInt32(0x00f2, &p32[0])) || (0 ==
(p32[0] & 1)) ) BAIL
if ( SUCCESS != fpgaReadInt32(0x00f6, &p32[0]) ) BAIL
if ( SUCCESS != fpgaReadInt32(0x00fa, &p32[1]) ) BAIL
sprintf(p + 0u, "%08lx", static_cast<long unsigned>(p32[0]));
sprintf(p + 16u, "%08lx", static_cast<long unsigned>(p32[1]));
return true;
# undef BAIL
}
I would have liked to use a lambda instead of a macro there, but then
I would have to write:
bool Serial(char *const p)
{
auto BAIL = [p](void)->void { strcpy(p, "0000000000000000"); };
int32_t p32[2u];
if ( (SUCCESS != fpgaReadInt32(0x00f2, &p32[0])) || (0 ==
(p32[0] & 1)) ) { BAIL(); return false; }
if ( SUCCESS != fpgaReadInt32(0x00f6, &p32[0]) ) { BAIL();
return false; }
if ( SUCCESS != fpgaReadInt32(0x00fa, &p32[1]) ) { BAIL();
return false; }
sprintf(p + 0u, "%08lx", static_cast<long unsigned>(p32[0]));
sprintf(p + 16u, "%08lx", static_cast<long unsigned>(p32[1]));
return true;
}
So I thinking, what if we had a 'long return lamda'? The return
statement from a 'long return lambda' is treated as a return statement
from the enclosing function. As follows:
bool Serial(char *const p)
{
auto BAIL = [p](void) -> long return bool { strcpy(p,
"0000000000000000"); return false; };
int32_t p32[2u];
if ( (SUCCESS != fpgaReadInt32(0x00f2, &p32[0])) || (0 ==
(p32[0] & 1)) ) BAIL();
if ( SUCCESS != fpgaReadInt32(0x00f6, &p32[0]) ) BAIL();
if ( SUCCESS != fpgaReadInt32(0x00fa, &p32[1]) ) BAIL();
sprintf(p + 0u, "%08lx", static_cast<long unsigned>(p32[0]));
sprintf(p + 16u, "%08lx", static_cast<long unsigned>(p32[1]));
return true;
}
Of course, you wouldn't be able to store one of these lambdas inside a
normal 'std::function', you'd need to store it inside a
'std::long_return_function' instead.
A few important points:
(1) The return type of a 'long return lambda', if not omitted, must be
implicitly-convertible to the return type of the enclosing function.
(2) You cannot analyse the return value from a 'long return lambda'
inside the enclosing function. For example the initialisation of a
variable inside the enclosing function cannot involve the invocation
of a 'long return lambda', so the following isn't valid:
int MyFunc(void)
{
auto monkey = []()->long return { return 5; }
int i = monkey(); // ill-formed - compiler must issue diagonstic
}
(3) However you can analyse the return value higher up the function
call hierarchy:
int Func2(void)
{
auto Bail = []() -> long return int { return 5; }
// int i = Bail(); - This would be invalid
Bail();
}
int Func(void)
{
int i = Func2(); // This is valid
}
Received on 2023-04-11 13:17:06