0

I have a non-copyable C++ lambda which captures a unique_ptr, and certain situations when compiling with Apple Clang as Objective-C++ cause the lambda to get converted to a block pointer, at which point the compilation fails due to an attempted copy of the lambda. A simple example is as follows:

int main(int argc, const char * argv[])
{   
    std::unique_ptr<int> myHeapInt = std::make_unique<int>(4);
    int myStackInt = 0;

    auto myLambda = [&, myHeapInt = std::move(myHeapInt)]()
    {
        myStackInt = *myHeapInt;
    };

    if(bool(myLambda)) //Error ar this point
    {
        *myHeapInt = 5;
    }

    std::invoke(myLambda);

    return 0;
}

The error is as follows:

Call to implicitly-deleted copy constructor of 'const lambda...
Implicit capture of lambda object due to conversion to block pointer here

Is there a way around this conversion?

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Rajveer
  • 847
  • 11
  • 28
  • 1
    Why is it that you think you can just mix and match C++ and Objective-C++? They are *different languages*. – Jesper Juhl Dec 11 '19 at 19:59
  • [Apparently you can](https://stackoverflow.com/questions/3684112/what-is-objective-c), but this looks like pure c++. – Ben Jones Dec 11 '19 at 20:04
  • Yes I know they are different languages, I am trying to make something compatible with both. – Rajveer Dec 11 '19 at 20:05
  • Rajveer, it is not a good idea to mix two languages. It is easier to make and manage two versions with "copy paste and adjust". Dropbox tried to make C++ based code for their mobile app (so it would work on both iPhone and Android) and came to conclusion that it is a bad idea. Now they just write two versions one for Android on Kotlin and one for Apple (on whatever language it natively uses... Swift?). – ALX23z Dec 11 '19 at 20:14
  • 1
    It's perfectly fine mixing C++ with Objective-C, this is what Objective-C++ is. – Rajveer Dec 11 '19 at 20:19
  • @Rajveer but if std::function doesn't compile can you really call it successful? – ALX23z Dec 11 '19 at 20:21
  • This isn't std::function, it's a std::function replacement which allows customisations such as supporting non-copyable lambdas. – Rajveer Dec 11 '19 at 20:26
  • `std::function` IIRC accepts non-copyable lambdas. – ALX23z Dec 11 '19 at 20:50
  • Just checked it - it doesn't accept it but there is a simple work around by wrapping the lambda. Google it, there is an answer on stackoverflow. – ALX23z Dec 11 '19 at 20:57

3 Answers3

3

What is that bool(myLambda)? I have no clue.

The only thing you can do with a lambda is evoke it: myLambda(). You cannot test for whether it exists or anything.

ALX23z
  • 4,456
  • 1
  • 11
  • 18
  • 3
    Technically you can cast a lambda to `bool` *if* it has no captures, because it can decay to a function pointer which can then convert to `bool`. However, to do so is pointless, because you can never have a falsy lambda. – 0x5453 Dec 11 '19 at 20:06
  • I am using a std::function replacement (https://github.com/Naios/function2) that used to compile fine as Objective-C++ but now fails to. This replacement does bool conversion as part of some optimisation, which is where it now fails since the lambda is converted to a block. – Rajveer Dec 11 '19 at 20:10
  • Same result unfortunately, Clang still tries to convert this to a block pointer. – Rajveer Dec 11 '19 at 20:31
  • @Rajveer I mean not just the bool cast but the if entirely. – ALX23z Dec 11 '19 at 20:49
0

So, I'm not entirely seeing the relevance of Objective-C++ here, as this code doesn't compile as C++ either:

objc++-noncopy-lambda.cpp:15:9: error: cannot convert '(lambda at objc++-noncopy-lambda.cpp:9:21)' to 'bool' without a conversion operator
    if (bool(myLambda))
        ^~~~~~~~~~~~~
1 error generated.

The error message is different; I assume there's some attempt at implicitly converting lambdas to blocks in Objective-C++, I've tried to stay away from weird edge cases like that, but it seems that in the absence of an operator bool it might try the conversion to a block first.

Either way the code you're attempting to write doesn't make any sense and the compiler is correctly rejecting it.

I see in the comments that you're actually trying to do something different. Could you perhaps post a reduced version of the code you're actually trying to write, which supposedly compiles as C++ but not as Objective-C++?

pmdj
  • 22,018
  • 3
  • 52
  • 103
  • What is your environment and what are you compilation options? My example works fine when compiled as C++17 with Clang, and the actual library compiles fine on MSVC, Clang and GCC (also compiled as C++17). It's not easy to create a reduced version because it's a templated header-file class, that's why I created the example above as that is the exact part that is failing. Essentially the class implements a vtable, which it optimises if the callable implements operator bool() or can be converted to bool. – Rajveer Dec 12 '19 at 20:22
  • Anyway I've found that this is by design in Clang for blocks and lambdas to be interoperable (http://clang.llvm.org/docs/LanguageExtensions.html#interoperability-with-c-11-lambdas) so I've just decided to disable this optimisation if compiled as Objective-C++. – Rajveer Dec 12 '19 at 20:24
0

I was trying to compile a templated header-file class replacement for std::function (github.com/Naios/function2) as Objective-C++, which implements a vtable and optimises it if the callable implements operator bool() or can be converted to bool.

In the end I just decided to disable this optimisation if compiled as Objective-C++ as converting to a block pointer is by design in Clang for block-lambda interoperability (http://clang.llvm.org/docs/LanguageExtensions.html#interoperability-with-c-11-lambdas).

Rajveer
  • 847
  • 11
  • 28