GNOME Bugzilla – Bug 672555
Deprecate lambda functions in libsigc++
Last modified: 2015-07-28 15:20:35 UTC
Lambda functions (anonymous functions, closures) are part of the C++11 standard. When most C++ compilers support those lambda functions, there's little or no reason to continue using any other type of lambda functions. I suggest that libsigc++'s lambda functions are deprecated. Copied from bug 669128 comment 3 by Murray Cumming: > I'm all for deprecating anything that is now provided by regular C++. It's > obviously easier to maintain libsigc++ when it is smaller. Any examples or > tests, if any, would have to be updated to show people how to change their > code. The syntax of the standardized C++ lambda functions is very different from the syntax of libsigc++'s lambda functions. ---- Useful links Wikipedia: http://en.wikipedia.org/wiki/Anonymous_function#C.2B.2B Bjarne Stroustrup's homepage: http://www2.research.att.com/~bs/C++0xFAQ.html#lambda The C++ Standards Committee: http://www.open-std.org/jtc1/sc22/wg21/ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf n3242.pdf is a draft of the C++11 standard. The approved standards document is not available for free, only for a fee.
There are some posts on libsigc-list showing that some C++11's lambda functions can't be assigned to a sigc::slot. http://mail.gnome.org/archives/libsigc-list/2011-July/msg00000.html There's a long thread on libsigc-list starting at http://mail.gnome.org/archives/libsigc-list/2011-August/msg00000.html and continuing at http://mail.gnome.org/archives/libsigc-list/2012-January/msg00000.html where there's a suggested solution to the problem. Here's an interesting comment at the end of the thread: http://mail.gnome.org/archives/libsigc-list/2012-January/msg00012.html "At least one case where the std::class is binary incompatible is std::list, which apparently gets a data member added so that size() becomes O(1). This change is apparently in GCC 4.7." This would mean that it's not safe to build a library file with -std=c++98 and use it in an application built with -std=c++0x or vice versa. (Options as in gcc 4.6.3.) ABI incompatibility between C++98 and C++11 can be a concern for all C++ modules (libsigc++, glibmm, gtkmm, etc.) during a transition period of perhaps a couple of years, before everyone uses only C++11.
Created attachment 219339 [details] [review] patch: Add SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH for C++11 lambda expressions. This patch adds a preprocessor macro with a template specialization of the struct sigc::functor_trait. To use this macro you need a compiler which has the C++11 keyword decltype or a keyword with the same meaning (such as typeof or __typeof__). Why not add a configure-time check instead of letting the application programmer decide whether to use this functionality? 1. The compiler capabilities depend on which options are specified at compile- time. E.g. if the libsigc++ library is configured on a system with gcc 4.6.3 using the default value of option -std, the result of a configure check is that decltype is not available. (__typeof__ is available, but I don't know about other compilers.) An application program may be compiled with -std=c++0x, and then decltype is available. 2. SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH can't be combined with SIGC_FUNCTORS_HAVE_RESULT_TYPE. If the functionality in SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH is activated by a configure check, it will break code that calls the old macro SIGC_FUNCTORS_HAVE_RESULT_TYPE. The patch also adds some documentation, mentioning that C++11 lambda expressions are often an alternative to libsigc++'s lambdas, and showing examples of C++11 lambdas. A test case with C++11 lambdas is added. This is meant as a preparation for a future deprecation of libsigc++'s lambdas. It's perhaps too early to deprecate them now. C++11 compilers are not yet available everywhere.
I've pushed the patch in comment 2 with some added comments in tests/test_cpp11_lambda.cc. http://git.gnome.org/browse/libsigc++2/commit/?id=41cf46e0fca8128abab298f54209d427a5a6c1b9
So, in general, the change here is that you can now use C++11 lambda functions if you first add this line? SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(decltype) What other possible things might someone use there instead of decltype?
(In replay to comment #4) > So, in general, the change here is that you can now use C++11 lambda > functions if you first add this line? > SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(decltype) Yes. > What other possible things might someone use there instead of decltype? gcc also understands the keywords typeof and __typeof__ which, I suppose, preceded the standardized decltype. Other compilers may define the same or similar non-standard keywords. Do you think it would be better to ignore all non-standard keywords, and replace SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(keyword) by SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE? Is such a replacement allowed as long as SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH( keyword) has not been included in a stable release?
(In reply to comment #5) > gcc also understands the keywords typeof and __typeof__ which, I suppose, > preceded the standardized decltype. Do we use these normally instead of this new thing? > Other compilers may define the same or > similar non-standard keywords. > > Do you think it would be better to ignore all non-standard keywords, and > replace SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(keyword) by > SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE? Well, does anybody want anything else? > Is such a replacement allowed as long as SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH( > keyword) has not been included in a stable release? Yes. I assume that we need this strange macro because later versions of g++ might be used for an app, but libsigc++ might have been built with an earlier ABI-compatible g++ version, so we can't just test for it in the libsigc++ configure, right?
(In reply to comment #6) >> Do you think it would be better to ignore all non-standard keywords, and >> replace SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(keyword) by >> SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE? > > Well, does anybody want anything else? I have replaced SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(keyword) by SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE. I see no good reason to start supporting non-standard keywords, synonymous with decltype. > I assume that we need this strange macro because later versions of g++ might > be used for an app, but libsigc++ might have been built with an earlier > ABI-compatible g++ version, so we can't just test for it in the libsigc++ > configure, right? Whether the compiler accepts decltype or not may depend on the compiler version or on the compiler parameters. g++ versions 4.6.3 and 4.7.0 accept decltype if invoked with the parameter -std=c++0x (or for g++ 4.7.0 -std=c++11). With the default value of -std (gnu++98), decltype is not accepted. Let's assume we replace the macro SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE by a configure check. If libsigc++ is configured with the default value of -std and --enable-warnings=fatal, a configure check would find that the compiler does not support decltype. If an application program is compiled with -std=c++11, it would be possible to use the template class specialization in SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE, but the configure check has hidden it from the compiler. It's possible that we can replace SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE by a compile-time check, like in tests/test_cpp11_lambda.cc. Something like the following code could be put in sigc++/functors/functor_trait.h. #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) Expansion of SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE #endif An additional problem is that it would break application programs that use the old macro SIGC_FUNCTORS_HAVE_RESULT_TYPE. Possibly that macro can be changed to something like #define SIGC_FUNCTORS_HAVE_RESULT_TYPE \ #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) \ // Nothing \ #else \ Present expansion of SIGC_FUNCTORS_HAVE_RESULT_TYPE \ #endif It's still possible that this trick would break some obscure application program. SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE does not support functors with overloaded function call operators, but I think SIGC_FUNCTORS_HAVE_RESULT_TYPE can do that. (Those are hand-written functors, not lambda expressions.) A compile-time check in functor_trait.h instead of a strange macro has its merits, of course. What do you think? Shall I implement the compile-time check despite the small risk that it will break some application program?
Created attachment 235504 [details] [review] patch: Add track_obj() and test_track_obj. The patch contains a new adaptor, track_obj(), that mitigates a disadvantage of the C++11 lambda expressions. By using track_obj() a slot can be auto- disconnected, if it contains a C++11 lambda expression that contains a reference to a sigc::trackable derived object, and the trackable object is deleted. It would be great if someone could test it with other compilers than gcc. If I get no objections within a few weeks or so, I'll push the patch.
I have pushed the patch in comment 8 with some insignificant changes. When is a good time to actually deprecate libsigc++'s lambda expressions? Now? Soon? I'll ask on libsigc-list too.
I have pushed a patch that deprecates everything in directory sigc++/adaptors/lambda: libsigc++ lambdas, sigc::group() and sigc::var(). https://git.gnome.org/browse/libsigc++2/commit/?id=ec27025f0ec5718c94427ea1fb51e968fba15d9e
I'd like to do a libsigc++ 2.5/2.6 soon that actually removes them, if nobody objects. It seems to all be in header files, so it will be an API change, not an ABI change.
Created attachment 307177 [details] [review] 0001-C-11-Avoid-the-need-for-SIGC_FUNCTORS_DEDUCE_RESULT_.patch Also, if we can know require C++11, can't we avoid the need for the application code to use SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE ? I've tried just turning the macro into a declaration, but make check fails with this patch: g++ -DHAVE_CONFIG_H -I.. -I.. -pedantic -Wall -Wextra -Werror -Wall -g -O0 -std=c++11 -MT test_bind_return.o -MD -MP -MF .deps/test_bind_return.Tpo -c -o test_bind_return.o test_bind_return.cc In file included from ../sigc++/adaptors/adaptor_trait.h:7:0, from ../sigc++/adaptors/bind_return.h:5, from test_bind_return.cc:7: ../sigc++/functors/functor_trait.h: In instantiation of ‘struct sigc::functor_trait<{anonymous}::foo, false>’: ../sigc++/adaptors/adaptor_trait.h:314:58: required from ‘struct sigc::adaptor_trait<{anonymous}::foo, false>’ ../sigc++/adaptors/adaptor_trait.h:388:59: required from ‘struct sigc::adapts<{anonymous}::foo>’ ../sigc++/adaptors/bind_return.h:20:8: required from ‘struct sigc::bind_return_functor<int, {anonymous}::foo>’ test_bind_return.cc:47:51: required from here ../sigc++/functors/functor_trait.h:140:88: error: decltype cannot resolve address of overloaded function typedef typename functor_trait<decltype(&T_functor::operator()), false>::result_type result_type; ^ In file included from test_bind_return.cc:7:0: ../sigc++/adaptors/bind_return.h: In instantiation of ‘typename sigc::unwrap_reference<T_type>::type sigc::bind_return_functor<T_return, T_functor>::operator()(T_arg1) [with T_arg1 = int; T_return = int; T_functor = {anonymous}::foo; typename sigc::unwrap_reference<T_type>::type = int]’: test_bind_return.cc:47:54: required from here ../sigc++/adaptors/bind_return.h:40:7: error: no matching function for call to ‘sigc::adaptor_functor<{anonymous}::foo>::operator()(int&)’ { this->functor_.SIGC_WORKAROUND_OPERATOR_PARENTHESES<typename type_trait<T_arg1>::pass> ^ ../sigc++/adaptors/bind_return.h:40:7: note: candidates are: In file included from ../sigc++/adaptors/bind_return.h:5:0, from test_bind_return.cc:7: ../sigc++/adaptors/adaptor_trait.h:88:3: note: template<class T_arg1> typename sigc::adaptor_functor<T_functor>::deduce_result_type<T_arg1>::type sigc::adaptor_functor<T_functor>::operator()(T_arg1) const [with T_arg1 = T_arg1; T_functor = {anonymous}::foo] operator()(T_arg1 _A_arg1) const ^ ../sigc++/adaptors/adaptor_trait.h:88:3: note: template argument deduction/substitution failed: ../sigc++/adaptors/adaptor_trait.h:107:3: note: template<class T_arg1, class T_arg2> typename sigc::adaptor_functor<T_functor>::deduce_result_type<T_arg1, T_arg2>::type sigc::adaptor_functor<T_functor>::operator()(T_arg1, T_arg2) const [with T_arg1 = T_arg1; T_arg2 = T_arg2; T_functor = {anonymous}::foo] operator()(T_arg1 _A_arg1, T_arg2 _A_arg2) const ^ ../sigc++/adaptors/adaptor_trait.h:107:3: note: template argument deduction/substitution failed:
Deprecation of libsigc++ lambdas was discussed on libsigc-list in February and March 2013, starting at https://mail.gnome.org/archives/libsigc-list/2013-February/msg00000.html. How about a notice on libsigc-list before they are deleted? There will probably not be much objections. > Also, if we can know require C++11, can't we avoid the need for the > application code to use SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE ? > I've tried just turning the macro into a declaration, but make check fails > with this patch: The informative part of the long compiler error message is test_bind_return.cc:47:51: required from here ../sigc++/functors/functor_trait.h:140:88: error: decltype cannot resolve address of overloaded function typedef typename functor_trait<decltype(&T_functor::operator()), false> ::result_type result_type; The foo struct in test_bind_return.cc has overloaded function call operators. I think it would be difficult or impossible to have the template class specialization template <typename T_functor> struct functor_trait<T_functor, false> { typedef typename functor_trait<decltype(&T_functor::operator()), false> ::result_type result_type; typedef T_functor functor_type; }; defined unconditionally. There are two problems with that, both mentioned in comments. 1. Functors with overloaded operator()() are not supported. 2. You can't use both SIGC_FUNCTORS_HAVE_RESULT_TYPE and SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE in the same compilation unit. I don't know if anyone uses SIGC_FUNCTORS_HAVE_RESULT_TYPE, except for libsigc++2/tests/test_compose.cc.
Created attachment 307315 [details] [review] patch: C++11: Avoid the need for SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE Perhaps it's possible after all. With a new template parameter in struct functor_trait<> and a new class can_deduce_result_type_with_decltype<>, it seems to be possible. Both 'make' and 'make check' succeed with g++ 4.9.2. As far as I can see, the added template parameter in functor_trait<> does not break ABI. There's no trace of functor_trait in the library files (.so files), neither in libsigc++ nor in gtkmm. SFINAE (Substitution Failure Is Not An Error) is a useful but obscure feature of C++. It tells the compiler to shut up and search for an alternative instead of reporting a compilation error. The substitution must fail in the declaration of an overloaded function.
Great. Would it be simpler if we remove the deprecated code first?
It doesn't matter what we do first, apply the patch in comment 14 or remove the files in sigc++/adaptors/lambda. The patch just changes a few comment lines in those files. I have tested the patch with clang++ 3.6.0. No problem. It would be fine if someone could test it with MSVC++, but even without such a test. I think it's reasonably safe to push it. I suppose I shall make a libsigc-2-4 branch before I push it to master.
Created attachment 307397 [details] [review] 0001-Remove-deprecated-sigc-lambda-API-that-is-only-in-he.patch This removes as much as (I think) can be removed while still allowing the code in the .cc file to build.
Sure. Please go ahead. Thanks.
Review of attachment 307315 [details] [review]: Pushed the patch in comment 14. How do you change the status of an attached patch without at the same time making a new comment? I could do it before the latest Bugzilla upgrade.
(In reply to Kjell Ahlstedt from comment #19) > How do you change the status of an attached patch without at the same time > making a new comment? > I could do it before the latest Bugzilla upgrade. By clicking "Details" next to the patch and then "Edit Details" at the top-right. I always forget how.
I played a bit with your patch in comment 17, removing lambda API. If it weren't for the sigc::_1 .. sigc::_7 objects in lambda.cc, the whole adaptors/lamba directory could be removed. Probably it can be removed anyway. sigc::_1 .. sigc::_7 are unusual objects. They have no member data, and all member functions are templates. The symbols sigc::_1 .. sigc::_7 (with mangled names) exist in libsigc-2.0.so.0.0.0, but none of them exists in test_lambda or lt-test_lambda, although sigc::_1 .. sigc::_3 are used in test_lambda.cc. Test steps: 1. make check Result: test_lambda is built and executed. Test passes. 2. Remove sigc++/adaptors/lambda/lambda.cc and references to it in other files. make && make install && make test Result: test_lambda is built and executed. Test passes. 3. Remove the whole sigc++/adaptors/lambda directory and references to files in it (except references from test_lambda.cc). Result: test_lambda still executes correctly, but it can't be rebuilt. Although sigc::_1 .. sigc::_7 exist in the .so file, it's as though they are not part of the ABI, only part of the API.
Ah, that's good news. Thanks. I'll get rid of it completely then. If we are wrong then we'll hear about it and can add it back, I guess.
Done in git master.
Lambdas and sigc::group() are mentioned in libsigc++'s website, http://libsigc.sourceforge.net/. It looks like libsigc++2/docs/website contains the source of that website. I don't know how it can be changed. It looks like incomplete html code. Firefox won't show it. (It does not show libsigc++2/docs/website/index.shtml. It does show libsigc.sourceforge.net.)