Var::NativeFunction as std::function

I know this will break backwards compatibility, but we are moving in the c++11 direction anyway.
Would be possible in the future to change the signature of var::NativeFunction to be a std::function ?

typedef var (*NativeFunction) (const NativeFunctionArgs&);

to

 typedef std::function<var (const NativeFunctionArgs&)> NativeFunction;

this way we can leverage lambdas with the DynamicObject::setMethod. Very convenient way of not subclassing DynamicObject multiple hundreds times for different objects.

3 Likes

Yes, will thoroughly enjoy changing that when we make the big move to C++11!

2 Likes

We could to this already now, couldn’t we?

   #if JUCE_COMPILER_SUPPORTS_LAMBDAS
    typedef std::function<var (const NativeFunctionArgs&)> NativeFunction;
   #else
    typedef var (*NativeFunction) (const NativeFunctionArgs&);
   #endif

I’ve done the same thing for RuntimePermissions::Callback for example.

Also makes it easier to find it later when we make the big move to C++11, we can search for those JUCE_COMPILER_SUPPORTS... macros

That sounds good, but wouldn’t it mean that the code that uses it would have to be different depending on the compiler?

yeah i’m not happy of this macro stuff, as things may change silently and you will end up having to use that JUCE_COMPILER_SUPPORTS_ macro in your code.

anyway i’m not going to embrace ever a pre-c++11 compiler…

a raw function pointer implicitly converts to a std::function, a lambda implicitly converts to a std::function, and a lambda without a capture implicitly converts to a raw function pointer. So I think it’s all fine. As long as the return type and argument types are equal you can use the same client code with both versions. Maybe I overlooked something?

I could be wrong here because I’ve not tried it but I don’t think it will break because a std::function can take a static function with the same signature as its argument. It will just wrap it up and forward the call to it.

It does however mean keeping two implementations as you’ll need both the pointer and std::function object in the class declaration/definition but I don’t think there’s that many places it’s used.

Why is that? NativeFunction is a typedef, right? So why can’t we use that typedef in the implementation instead of explicitly writing two versions?

Yeah… it might work. I just have a feeling that there’d be an edge-case problem somewhere!

yeah exactly. here is the offending problem:

juce_Variant.h:310:24: 

Copy assignment operator of 'ValueUnion' is implicitly deleted
because variant field 'methodValue' has a non-trivial copy assignment
operator

need to iron out a workaround

Yeah, meditating a bit more on this code, the C++11 standard says:

9.5 Unions [class.union]

2 […] If any non-static data member of a union has a non-trivial default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8), move assignment operator (12.8), or destructor (12.4), the corresponding member function of the union must be user-provided or it will be implicitly deleted (8.4.3) for the union.

And std::function is non-trivial (a raw function pointer is). Therefore std::function can’t be stored in a union like that. You’d need to explicitly implement a copy constructor, copy assignment etc. for var::ValueUnion for this to work, just like the hack suggested further down in this stackoverflow post: c++ - Why compiler doesn't allow std::string inside union? - Stack Overflow

i would prefer a “smart” backward compatible move.

I don’t see how this would work? Could you please explain a bit more what exactly you mean, that would be really helpful!

We already do some C++11 stuff that can be kept backward compatible (nullptr , override etc). But If we really want to leverage lambdas, variadic templates, move semantics etc. in common JUCE code, we need to either sprinkle the whole code with more #ifdef’s and maintain double implementations everywhere (not really an option I think :wink: ) or we need to break compatibility with C++98 and pre-2013 Visual Studio at some point.

So the actual question is really: should we do it or not?

1 Like

I’m all for breaking compatibility. I’d rather my code suddenly stop compiling than silently be behaving differently depending on the compiler.

We already use C++11 in Tracktion and as far as I can tell the only platform we can no longer target is < OS X 10.7. Not really a big deal. I’ve only had two customers ask for it in 2 years.

The real problem is that it’s going to be a big change if done fully i.e. replace all ScopedPointer’s with std::unique_ptr. So I’d rather see this done gradually so we can update our code in increments (even if all the JUCE commits come at once, it would be nice to have them split up so we can mirror the process).

…? really curious to know the answer… suspense :slight_smile: My guess is that you mean OS X 10.6?

Also, what about AAX plug-ins and the whole Pace signing stuff that comes with it, is that all fully compatible with the newest compilers and C++11 on both OS X and Windows?

Strange, typing the following without the space stopped that line from appearing.
I’ve edited the post now to display the rest of the sentence.

I presume so, we ship BioTek as AAX on both those platforms and that uses C++11 too.

Yeah I also know of companies that successfully build and ship AAX plug-ins using VS2013 on Windows, and -std=c++11 on Xcode/Mac. However I also heard some people claiming that it would not work. So I’m not sure now what the exact situation is.

If OS X 10.6 is really the only platform we would break by going C++11, that’d be totally OK imho. People will just have to update their compilers, which is a good idea anyway, and obviously we’d announce this well in advance :wink:

Its very simple, please keep sure that old code, can be still compiled with latest juce and works exactly like before.

This is what a framework should be, reliable.

Please don’t underestimate the importance of reliability.

Please!

And please no general-ism, sometimes some well documented #ifdefs are okay!

Add c++11 features step by step, and always optional, so that existing code, can be compiled with older juce-releases to, to cross check possible new bugs.

Once you make “API” changes, its not possible to switch back and forth, and than may we have a clean and cool c++11 juce, but a complete messed project in “user-space”.

The new plugin-api changes are a perfect example what to do not, please don’t make the same mistake again.

1 Like

in the meanwhile… https://github.com/julianstorer/JUCE/pull/73