I am new to IOS programming, and am learning about blocks and lambdas. In my research on blocks and c++ lambdas I read that essentially lambdas can replace blocks because they can do pretty much the same thing. This appears to be case except for the capture clause and the C++ specific (mutable and throw) optional keywords. I have found some differences that might make a difference – but not sure. I can create a std::function wrapper type variable (lambda) and assign a block and its equivalent lambda statement(s). However, if I create a block type variable and attempt to assign a lambda to it – the compile terminates with an error. Why? I’ll show the code and the error output below. Also, I cannot typedef a lambda, I must use a std::function wrapper function type to do the equivalent. Why?
#include <iostream>
#include <functional>
#include <pthread.h>
#include <xpc/xpc.h>
#include <dispatch/dispatch.h>
#define SHOW_COMPILE_ERROR
// add comments on next line to see the compile error
#undef SHOW_COMPILE_ERROR
// You cannot create a lambda typedef, but you can create a general purpose
// polymorphic function wrapper from std::wrapper
// e.g. typedef [] (xpc_object_t) -> void do_custom_message_l_t; is not legal
// but the std::function declaration is legal - why?
typedef std::function<void(xpc_object_t)> do_custom_msg_l_t;
// block typedefs are legal as below
typedef void (^do_custom_msg_b_t)(xpc_object_t);
// create a function declaration with a callback using either
// approaches - the block typedef or the std::function wrapper
// just as well
xpc_object_t build_message(int func,
do_custom_msg_b_t&);
xpc_object_t build_message(int func,
do_custom_msg_l_t&);
// create a couple of global variables of the block or std::function type
do_custom_msg_b_t do_custom_msg_b;
do_custom_msg_l_t do_custom_msg_l, do_custom_msg_l2;
#if defined(SHOW_COMPILE_ERROR)
do_custom_msg_b_t do_custom_msg_b2;
#endif
int main(int argc, char *argv[])
{
// assign a block to the block type global
do_custom_msg_b = ^(xpc_object_t custom_message) {
auto p= xpc_copy_description(custom_message);
std::cerr << "Custom Message Objects: " << p << std::endl;
free(p);
};
// assign a std::function type a lambda definition - cool.
// but change the variable to do_custom_msg_b and suddenly there
// is an error! So you can assign a lambda definition to a block
// variable type. Very weird.
do_custom_msg_l = [] (xpc_object_t custom_message) {
auto p= xpc_copy_description(custom_message);
std::cerr << "Custom Message Objects: " << p << std::endl;
free(p);
};
#if defined(SHOW_COMPILE_ERROR)
// this is not legal - why?, if it is legal to assign a block to a lambda
// /std::function then why not the other way around?
do_custom_msg_b2 = [] (xpc_object_t custom_message) {
auto p= xpc_copy_description(custom_message);
std::cerr << "Custom Message Objects: " << p << std::endl;
free(p);
};
#endif
// we are now assigning a block to std::function wrapper (lambda)
// this works just fine . . . weird!
do_custom_msg_l2 = ^(xpc_object_t custom_message) {
auto p= xpc_copy_description(custom_message);
std::cerr << "Custom Message Objects From Blocks Assigned to std::function: " << p << std::endl;
free(p);
};
// in conclusion blocks can be assigned lambda/std::functions but std::functions/lambdas
// cannot be assigned to block types (typedefs). So there is a false equivilence between
// lambdas and blocks
// here is how lambdas are normally defined and executing them
// is very easy
auto mylambda = [] (void *ptr){
std::cout << "This lambda " << ptr << " is running in thread " << pthread_self() << std::endl;
sleep(1);
};
// here is the block equivalent to the above lambda
void (^myblock) (void) = ^{
std::cout << "This block is running in thread " << pthread_self() << std::endl;
sleep(1);
};
// call that lambda with its address as a parameter
mylambda(&mylambda);
// call the block
myblock();
// dispatch the lambda - cool!
dispatch_async_f(dispatch_get_main_queue(), &mylambda, mylambda);
// dispatch the block - very cool!
dispatch_async(dispatch_get_main_queue(), myblock);
// build an object using the block callback
xpc_object_t msg = build_message(1, do_custom_msg_b);
// build a message utilizing the lambda call
msg = build_message(2, do_custom_msg_l);
// call the lambda variable that was assigned a block
// this works - very cool!
msg = build_message(3, do_custom_msg_l2);
#if defined(SHOW_COMPILE_ERROR)
// nope, this won't even build
msg = build_message(4, do_custom_msg_b2);
#endif
dispatch_main();
return 0;
}
/**
* @brief build a new message header to be sent to the server
* @param func The function id of what command to be sent
* @param do_custom_msg block that can optionally customize this created message
* @returns the xpc object built
* @note objects created with this function must be released
*/
xpc_object_t build_message(int func,
do_custom_msg_b_t& do_custom_msg)
{
xpc_object_t msg = xpc_dictionary_create_empty();
xpc_dictionary_set_uint64(msg, "typ", func);
xpc_dictionary_set_date(msg, "date", time(nullptr));
xpc_dictionary_set_uint64(msg, "serno",
1);
if(nullptr != do_custom_msg)
{
do_custom_msg(msg);
}
return msg;
}
/**
* @brief build a new message header to be sent to the server
* @param func The function id of what command to be sent
* @param do_custom_msg block that can optionally customize this created message
* @returns the xpc object built
* @note objects created with this function must be released
*/
xpc_object_t build_message(int func,
do_custom_msg_l_t& do_custom_message )
{
xpc_object_t msg = xpc_dictionary_create_empty();
xpc_dictionary_set_uint64(msg, "typ", func);
xpc_dictionary_set_date(msg, "date", time(nullptr));
xpc_dictionary_set_uint64(msg, "serno",
1);
if(nullptr != do_custom_message)
{
do_custom_message(msg);
}
return msg;
}
Here are the particulars of my environment:
CLion 2024.1.4.
Apple clang version 15.0.0
2023 Mac Studio running Sonoma 14.6.1
Here is the error message:
/Applications/CLion.app/Contents/bin/cmake/mac/aarch64/bin/cmake --build "/Users/marksanderson/Projects/codeing tests/cmake-build-debug" --target codeing_tests -j 22
[1/2] Building CXX object CMakeFiles/codeing_tests.dir/main.cpp.o
FAILED: CMakeFiles/codeing_tests.dir/main.cpp.o
/Library/Developer/CommandLineTools/usr/bin/c++ -g -std=gnu++20 -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX14.4.sdk -fcolor-diagnostics -MD -MT CMakeFiles/codeing_tests.dir/main.cpp.o -MF CMakeFiles/codeing_tests.dir/main.cpp.o.d -o CMakeFiles/codeing_tests.dir/main.cpp.o -c '/Users/marksanderson/Projects/codeing tests/main.cpp'
/Users/marksanderson/Projects/codeing tests/main.cpp:59:24: error: assigning to 'do_custom_msg_b_t' (aka 'void (^)(xpc_object_t)') from incompatible type '(lambda at /Users/marksanderson/Projects/codeing tests/main.cpp:59:24)'
do_custom_msg_b2 = [] (xpc_object_t custom_message) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
ninja: build stopped: subcommand failed.