After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 753612 - Replace generated templates with C++11/14 variadic templates.
Replace generated templates with C++11/14 variadic templates.
Status: RESOLVED FIXED
Product: libsigc++
Classification: Bindings
Component: general
unspecified
Other Linux
: Normal normal
: ---
Assigned To: libsigc++ maintainer(s)
libsigc++ maintainer(s)
Depends on:
Blocks:
 
 
Reported: 2015-08-14 09:56 UTC by Murray Cumming
Modified: 2016-03-07 10:28 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
0001-C-11-ptr_fun.h-Replace-generated-pointer_functor1-2-.patch (6.95 KB, patch)
2015-08-14 10:02 UTC, Murray Cumming
none Details | Review
0002-C-11-ptr_fun.h-Replace-generated-ptr_fun1-2-3-etc-wi.patch (8.37 KB, patch)
2015-08-14 10:03 UTC, Murray Cumming
needs-work Details | Review
0001-C-11-deduce_result_type-Simplify-with-variadic-templ.patch (9.28 KB, patch)
2015-08-14 21:24 UTC, Murray Cumming
committed Details | Review
0001-C-11-deduce_result_type-build-fix-using-SFINAE (2.68 KB, patch)
2015-08-15 06:52 UTC, Marcin Kolny (IRC: loganek)
committed Details | Review
0001-bind.h.m4-Try-to-make-some-operator-variadic.patch (6.00 KB, patch)
2016-01-09 19:16 UTC, Murray Cumming
rejected Details | Review
0001-signal.h-Trying-to-use-std-result_of.patch (1.15 KB, patch)
2016-01-15 10:14 UTC, Murray Cumming
none Details | Review
0001-bind-Try-to-make-this-variadic-instead-of-generated.patch (41.44 KB, patch)
2016-01-15 20:41 UTC, Murray Cumming
needs-work Details | Review
automatic deduction accumulator's operator() return type (1.01 KB, patch)
2016-01-15 20:46 UTC, Marcin Kolny (IRC: loganek)
committed Details | Review
partial build fix (1.88 KB, patch)
2016-01-18 22:33 UTC, Marcin Kolny (IRC: loganek)
none Details | Review

Description Murray Cumming 2015-08-14 09:56:35 UTC
libsigc++ uses m4 files to generate .h files that contain versions of the same templates with various numbers of arguments. For instance, sigc::slot0<>, sigc::slot1<>. sigc::ptr_fun0<>(), sigc::ptr_fun1<>(), We generally hide these from users of the API via things like sigc::slot<>, sigc::ptr_fun() and sigc::mem_fun().

We can probably replace them all with C++11 variadic templates, as used in C++11's std::function.

Unfortunately, this would break ABI of libraries, such as gtkmm, which use libsigc++. For instance, gtkmm seems to export symbols that use sigc::slot* and pointer_functor*, probably via its use of sigc::slot<> as method parameters:

$ nm -D --demangle  gtk/gtkmm/.libs/libgtkmm-3.0.so | grep "pointer_functor"
0000000000523a4a W void sigc::visit_each<sigc::internal::limit_derived_target<sigc::trackable*, sigc::internal::slot_do_bind>, sigc::bind_functor<-1, sigc::pointer_functor3<Glib::RefPtr<Gdk::Screen> const&, std::vector<Gdk::Color, std::allocator<Gdk::Color> > const&, void (*)(_GdkScreen*, _GdkColor const*, int), void>, void (*)(_GdkScreen*, _GdkColor const*, int), sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil> >(sigc::internal::limit_derived_target<sigc::trackable*, sigc::internal::slot_do_bind> const&, sigc::bind_functor<-1, sigc::pointer_functor3<Glib::RefPtr<Gdk::Screen> const&, std::vector<Gdk::Color, std::allocator<Gdk::Color> > const&, void (*)(_GdkScreen*, _GdkColor const*, int), void>, void (*)(_GdkScreen*, _GdkColor const*, int), sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil> const&)
0000000000523d9b W void sigc::visit_each<sigc::internal::limit_derived_target<sigc::trackable*, sigc::internal::slot_do_bind>, sigc::adaptor_functor<sigc::pointer_functor3<Glib::RefPtr<Gdk::Screen> const&, std::vector<Gdk::Color, std::allocator<Gdk::Color> > const&, void (*)(_GdkScreen*, _GdkColor const*, int), void> > >(sigc::internal::limit_derived_target<sigc::trackable*, sigc::internal::slot_do_bind> const&, sigc::adaptor_functor<sigc::pointer_functor3<Glib::RefPtr<Gdk::Screen> const&, std::vector<Gdk::Color, std::allocator<Gdk::Color> > const&, void (*)(_GdkScreen*, _GdkColor const*, int), void> > const&)
000000000053f3dc W void sigc::visit_each<sigc::internal::limit_derived_target<sigc::trackable*, sigc::internal::slot_do_bind>, sigc::adaptor_functor<sigc::pointer_functor4<Glib::ustring const&, Glib::ustring const&, int, Glib::RefPtr<Gtk::TreeModel> const&, void> > >(sigc::internal::limit_derived_target<sigc::trackable*, sigc::internal::slot_do_bind> const&, sigc::adaptor_functor<sigc::pointer_functor4<Glib::ustring const&, Glib::ustring const&, int, Glib::RefPtr<Gtk::TreeModel> const&, void> > const&)
0000000000524018 W void sigc::visit_each<sigc::internal::limit_derived_target<sigc::trackable*, sigc::internal::slot_do_bind>, sigc::pointer_functor3<Glib::RefPtr<Gdk::Screen> const&, std::vector<Gdk::Color, std::allocator<Gdk::Color> > const&, void (*)(_GdkScreen*, _GdkColor const*, int), void> >(sigc::internal::limit_derived_target<sigc::trackable*, sigc::internal::slot_do_bind> const&, sigc::pointer_functor3<Glib::RefPtr<Gdk::Screen> const&, std::vector<Gdk::Color, std::allocator<Gdk::Color> > const&, void (*)(_GdkScreen*, _GdkColor const*, int), void> const&)
000000000053f96c W void sigc::visit_each<sigc::internal::limit_derived_target<sigc::trackable*, sigc::internal::slot_do_bind>, sigc::pointer_functor4<Glib::ustring const&, Glib::ustring const&, int, Glib::RefPtr<Gtk::TreeModel> const&, void> >(sigc::internal::limit_derived_target<sigc::trackable*, sigc::internal::slot_do_bind> const&, sigc::pointer_functor4<Glib::ustring const&, Glib::ustring const&, int, Glib::RefPtr<Gtk::TreeModel> const&, void> const&)
...


However, I'd still like to see how we could use variadic templates to simplify libsigc++. It would more clearly show what libsigc++ offers that is not in regular C++11. And maybe we could do an ABI-breaking release of the *mm libraries one day.
Comment 1 Murray Cumming 2015-08-14 10:02:42 UTC
Created attachment 309248 [details] [review]
0001-C-11-ptr_fun.h-Replace-generated-pointer_functor1-2-.patch

C++11: ptr_fun.h: Replace generated pointer_functor1/2/3/etc with pointer_functor.
    
    Using C++11 variadic templates arguments.
    Note that T_Return is now the first template parameter, so that the variadic
    parameters can be the trailing parameters, as in std::function.

This seems to work fine, but we can't use it because I think it would break glibmm and gtkmm ABI.
Comment 2 Murray Cumming 2015-08-14 10:03:53 UTC
Created attachment 309249 [details] [review]
0002-C-11-ptr_fun.h-Replace-generated-ptr_fun1-2-3-etc-wi.patch

C++11: ptr_fun.h: Replace generated ptr_fun1/2/3/etc with ptr_fun<>.
    
    Note that T_Return must now be the first parameter, so that the
    variadic template parameters may be trailing, and this means that
    the return type must now be specified if you specify any argument
    types.
    For instance:
      ptr_fun<type_arg1>(&somefunc)
    now becomes
      ptr_fun<void, type_arg2>(&somefunc)
    
    and
      ptr_fun<type_arg1, type_return>(&somefunc)
    now becomes
      ptr_fun<type_return, type_arg1>(&somefunc)
    
    which might be an annoying API change, even though most people just
    use ptr_fun().
    
    However, this breaks the tests:
    
    test_ptr_fun.cc: In function ‘int main(int, char**)’:
    test_ptr_fun.cc:64:21: error: no matches converting function ‘foo’ to type ‘void (*)()’
       sigc::ptr_fun(&foo)();
                         ^
    test_ptr_fun.cc:24:6: note: candidates are: void {anonymous}::foo(int)
     void foo(int i1)
          ^
    test_ptr_fun.cc:18:5: note:                 int {anonymous}::foo()
     int foo()
         ^
Comment 3 Murray Cumming 2015-08-14 21:24:57 UTC
Created attachment 309308 [details] [review]
0001-C-11-deduce_result_type-Simplify-with-variadic-templ.patch

C++11: deduce_result_type: Simplify with variadic template and std::conditional<>.
    
    However, the build then fails like so:
    
    make[2]: Entering directory '/home/murrayc/checkout/gnome/libsigc++2/examples'
    g++ -DHAVE_CONFIG_H   -I.. -I..  -pedantic -Wall -Wextra -Wshadow -Wformat-security -Werror -Wall -g -O0 -std=c++11 -MT hello_world.o -MD -MP -MF .deps/hello_world.Tpo -c -o hello_world.o hello_world.cc
    In file included from ../sigc++/adaptors/adaptor_trait.h:10:0,
                     from ../sigc++/functors/slot.h:7,
                     from ../sigc++/signal_base.h:27,
                     from ../sigc++/signal.h:8,
                     from ../sigc++/sigc++.h:86,
                     from hello_world.cc:10:
    ../sigc++/adaptors/deduce_result_type.h: In instantiation of ‘struct sigc::deduce_result_type<sigc::pointer_functor1<const std::basic_string<char>&, void>, const std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, void, void, void, void, void, void>’:
    ../sigc++/adaptors/deduce_result_type.h:60:80:   required by substitution of ‘template<class T_functor, class ... T_args> using deduce_result_t = typename sigc::deduce_result_type::type [with T_functor = sigc::pointer_functor1<const std::basic_string<char>&, void>; T_args = {const std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, void, void, void, void, void, void}]’
    ../sigc++/adaptors/adaptor_trait.h:67:104:   required from ‘struct sigc::adaptor_functor<sigc::pointer_functor1<const std::basic_string<char>&, void> >::deduce_result_type<const std::basic_string<char>&, void, void, void, void, void, void>’
    ../sigc++/adaptors/adaptor_trait.h:88:3:   required by substitution of ‘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 = const std::basic_string<char>&]’
    ../sigc++/functors/slot.h:137:20:   required from ‘static T_return sigc::internal::slot_call1<T_functor, T_return, T_arg1>::call_it(sigc::internal::slot_rep*, sigc::type_trait_take_t<T_arg3>) [with T_functor = sigc::pointer_functor1<const std::basic_string<char>&, void>; T_return = void; T_arg1 = const std::basic_string<char>&; sigc::type_trait_take_t<T_arg3> = const std::basic_string<char>&]’
    ../sigc++/functors/slot.h:144:37:   required from ‘static void* (* sigc::internal::slot_call1<T_functor, T_return, T_arg1>::address())(void*) [with T_functor = sigc::pointer_functor1<const std::basic_string<char>&, void>; T_return = void; T_arg1 = const std::basic_string<char>&; sigc::internal::hook = void* (*)(void*)]’
    ../sigc++/functors/slot.h:529:91:   required from ‘sigc::slot1<T_return, T_arg1>::slot1(const T_functor&) [with T_functor = sigc::pointer_functor1<const std::basic_string<char>&, void>; T_return = void; T_arg1 = const std::basic_string<char>&]’
    ../sigc++/functors/slot.h:1161:26:   required from ‘sigc::slot<T_return, T_arg1, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil>::slot(const T_functor&) [with T_functor = sigc::pointer_functor1<const std::basic_string<char>&, void>; T_return = void; T_arg1 = const std::basic_string<char>&]’
    hello_world.cc:25:50:   required from here
    ../sigc++/adaptors/deduce_result_type.h:56:12: error: no class template named ‘deduce_result_type’ in ‘class sigc::pointer_functor1<const std::basic_string<char>&, void>’
         >::type;
                ^
    In file included from ../sigc++/signal_base.h:27:0,
                     from ../sigc++/signal.h:8,
                     from ../sigc++/sigc++.h:86,
                     from hello_world.cc:10:
    ../sigc++/functors/slot.h: In instantiation of ‘static T_return sigc::internal::slot_call1<T_functor, T_return, T_arg1>::call_it(sigc::internal::slot_rep*, sigc::type_trait_take_t<T_arg3>) [with T_functor = sigc::pointer_functor1<const std::basic_string<char>&, void>; T_return = void; T_arg1 = const std::basic_string<char>&; sigc::type_trait_take_t<T_arg3> = const std::basic_string<char>&]’:
    ../sigc++/functors/slot.h:144:37:   required from ‘static void* (* sigc::internal::slot_call1<T_functor, T_return, T_arg1>::address())(void*) [with T_functor = sigc::pointer_functor1<const std::basic_string<char>&, void>; T_return = void; T_arg1 = const std::basic_string<char>&; sigc::internal::hook = void* (*)(void*)]’
    ../sigc++/functors/slot.h:529:91:   required from ‘sigc::slot1<T_return, T_arg1>::slot1(const T_functor&) [with T_functor = sigc::pointer_functor1<const std::basic_string<char>&, void>; T_return = void; T_arg1 = const std::basic_string<char>&]’
    ../sigc++/functors/slot.h:1161:26:   required from ‘sigc::slot<T_return, T_arg1, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil>::slot(const T_functor&) [with T_functor = sigc::pointer_functor1<const std::basic_string<char>&, void>; T_return = void; T_arg1 = const std::basic_string<char>&]’
    hello_world.cc:25:50:   required from here
    ../sigc++/functors/slot.h:137:20: error: no matching function for call to ‘sigc::adaptor_functor<sigc::pointer_functor1<const std::basic_string<char>&, void> >::operator()(const std::basic_string<char>&)’
                    (a_1);
                        ^
    ../sigc++/functors/slot.h:137:20: note: candidates are:
    In file included from ../sigc++/functors/slot.h:7:0,
                     from ../sigc++/signal_base.h:27,
                     from ../sigc++/signal.h:8,
                     from ../sigc++/sigc++.h:86,
                     from hello_world.cc:10:
    ../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 = sigc::pointer_functor1<const std::basic_string<char>&, void>]
       operator()(T_arg1 _A_arg1) const
       ^
    ../sigc++/adaptors/adaptor_trait.h:88:3: note:   substitution of deduced template arguments resulted in errors seen above
    ../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 = sigc::pointer_functor1<const std::basic_string<char>&, void>]
       operator()(T_arg1 _A_arg1, T_arg2 _A_arg2) const
       ^
    ../sigc++/adaptors/adaptor_trait.h:107:3: note:   template argument deduction/substitution failed:
    In file included from ../sigc++/signal_base.h:27:0,
                     from ../sigc++/signal.h:8,
                     from ../sigc++/sigc++.h:86,
                     from hello_world.cc:10:
    ../sigc++/functors/slot.h:137:20: note:   candidate expects 2 arguments, 1 provided
                    (a_1);
                        ^
Comment 4 Marcin Kolny (IRC: loganek) 2015-08-15 00:22:49 UTC
Hi,
I'm not familiar with sigc++2 library (yet), but I tried to analyze your error.
I simplified code a little. You're trying to something like this:

struct Base {};
struct A : public Base
{
  typedef int X;
};
struct B
{
};
template<typename T>
struct traits
{
  typedef double Y;
};
template<typename T>
using my_type = typename std::conditional<std::is_base_of<Base, T>::value, typename T::X, typename traits<T>::Y>::type;

Code shown below works for A, because my_type equals std::conditional<true, A::X, traits<A>::Y>. Both: A::X and traits<A>::Y are valid, and std::conditional returns first type (because first template argument equals true).
But lets try the same with type B:
std::conditional<true, B::X, traits<B>::Y>
traits<B>::Y is valid, but B::X is not. For std::conditional, both types have to exist, even if one of them is not chosen.

Possible solution: SFINAE (https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error)

  template<class T>
  struct get_type {
    template<class U, typename = typename U::X>
    static typename U::X  test (int);

    template<class U>
    static typename traits<T>::Y test (...);
    using type = decltype (test<T> (0));
  };

  std::cout << typeid(get_type<A>::result_type).name() << '\n'; // i
  std::cout << typeid(get_type<B>::result_type).name() << '\n'; // d

Compiler will choose best valid instantiation, so if typename U::X fails, compiler's going to choose second one.

It's quite late, but at morning I'll try to prepare a patch for sigc++2.

Btw. I'd recommend you to use clang instead of gcc (variables CC=clang CXX=clang++) at least for template errors. It gives you clearer output.
Comment 5 Marcin Kolny (IRC: loganek) 2015-08-15 00:24:21 UTC
s/result_type/type
Comment 6 Marcin Kolny (IRC: loganek) 2015-08-15 06:52:36 UTC
Created attachment 309319 [details] [review]
0001-C-11-deduce_result_type-build-fix-using-SFINAE
Comment 7 Murray Cumming 2015-08-20 09:06:14 UTC
Thanks. Why do we need the std::is_same() there? Isn't that just giving us a true_type for a true_type?
Comment 8 Murray Cumming 2015-08-20 09:32:58 UTC
Thanks. This one removes the extra is_same<>, adds the missing ::type, and removes the method parameters. I was particularly confused that one was int and one was ... .

template<class T_functor, class... T_args>
struct deduce_result_type
{
  //The compiler will choose this method overload if T_functor derives from adaptor_base,
  //and if it has its own deduce_result_type member (which has its own ::type member).
  template<class U_functor, typename = typename std::is_base_of<adaptor_base, T_functor>::type>
  static
  typename U_functor::template deduce_result_type<T_args...>::type
  test();

  //Otherwise, the compiler will choose this fallback method.
  template<class U_functor>
  static
  typename functor_trait<T_functor>::result_type
  test();

  using type = decltype (test<T_functor> ());
};

It seems to work in g++ and clang++, with all tests passing.

I wonder if there's any way to remove the extra U_functor template type, which is always the same as T_functor.
Comment 9 Marcin Kolny (IRC: loganek) 2015-08-23 22:33:49 UTC
(In reply to Murray Cumming from comment #7)
> Thanks. Why do we need the std::is_same() there? Isn't that just giving us a
> true_type for a true_type?
Oh, I have no idea why did I use is_same there...

(In reply to Murray Cumming from comment #8)
>   template<class U_functor, typename = typename
>   std::is_base_of<adaptor_base, T_functor>::type>
>   static
>   typename U_functor::template deduce_result_type<T_args...>::type
>   test();
Actually, it doesn't work as expected. Code shown above only checks, if deduce_result_type<T_args...>::type exists. std::is_base_of doesn't affect on result. So even if class T_functor isn't derived from adaptor_base, but defines deduce_result_type<>::type, this case is chosen, and function_trait hasn't be used. 
Obviously, it works both ways: even if some class is not derived from adaptor_base, but this class contains deduce_result_type<...>::type definition, compiler will use this definition instead of function_trait.

So we can just skip is_base_of in code:

template<class U_functor>
static typename U_functor::template deduce_result_type<T_args...>::type test();

However, I don't know whether it is valid behavior or not. If it's not, we should use following paradigm: 

struct Base {};
struct A : public Base
{
  typedef int X;
};
struct B
{
  typedef float X;
};
template<typename T>
struct traits
{
  typedef double Y;
};

template<typename T>
typename T::X test(std::true_type);
template<typename T>
typename traits<T>::Y test(std::false_type);

template<class T>
class get_type
{
public:
  using result_type = decltype(test<T>(typename std::is_base_of<Base, T>()));
};

int main ()
{
  std::cout << typeid(get_type<A>::result_type).name() << '\n'; // i
  std::cout << typeid(get_type<B>::result_type).name() << '\n'; // d
}

Even if B::X exists, traits<>::Y will be used (because B is not derived from Base). But as I said, I don't know what behavior do we expect from libsigc++2, so I didn't modify a patch yet. 
 
> I wonder if there's any way to remove the extra U_functor template type,
> which is always the same as T_functor.
Hmm, I'm afraid there is no way to remove it. If we use T_functor::template deduce_result_type<T_args...>::type somewhere in code, compiler will always check, if this type exists (and fails, if it doesn't).
Comment 10 Murray Cumming 2015-09-17 13:03:03 UTC
I've pushed the first two deduce_result_type() patches (mine and yours to fix it) and my simplification:
https://git.gnome.org/browse/libsigc++2/commit/?id=960f48920ea86d9dab2c84344992c19b55895f20

I think it works much like the old code, even if we don't quite know why it should work that way. For now, I'd rather leave it at that. If you have an improvement, please do suggest a patch.
Comment 11 Murray Cumming 2016-01-07 10:30:31 UTC
(In reply to Murray Cumming from comment #2)
> Created attachment 309249 [details] [review] [review]
> 0002-C-11-ptr_fun.h-Replace-generated-ptr_fun1-2-3-etc-wi.patch
> 
> C++11: ptr_fun.h: Replace generated ptr_fun1/2/3/etc with ptr_fun<>.
[snip]
>     However, this breaks the tests:
>     
>     test_ptr_fun.cc: In function ‘int main(int, char**)’:
>     test_ptr_fun.cc:64:21: error: no matches converting function ‘foo’ to
> type ‘void (*)()’
>        sigc::ptr_fun(&foo)();
>                          ^
>     test_ptr_fun.cc:24:6: note: candidates are: void {anonymous}::foo(int)
>      void foo(int i1)
>           ^
>     test_ptr_fun.cc:18:5: note:                 int {anonymous}::foo()
>      int foo()
>          ^

The two foo() overloads differer by return type as well as by the number of parametrers. I can fix the build by specifying the return types, like so:

-  sigc::ptr_fun(&foo)();
+  sigc::ptr_fun<int>(&foo)();
 
-  sigc::ptr_fun(&foo)(1);
+  sigc::ptr_fun<void>(&foo)(1);

but this doesn't seem like it should be necessary. This is the ptr_fun template:

template <class T_return, class... T_args>
inline pointer_functor<T_return, T_args...> 
ptr_fun(T_return (*_A_func)(T_args...))
{ return pointer_functor<T_return, T_args...>(_A_func); }
Comment 12 Murray Cumming 2016-01-07 21:34:58 UTC
I'm gradually trying to remove all the m4 code generation, by using variadic templates, in the variadic branch:
https://git.gnome.org/browse/libsigc++2/log/?h=variadic
Comment 13 Murray Cumming 2016-01-07 22:36:15 UTC
I'm a bit stuck with templates that take an optional accumulator.
http://libsigc.sourceforge.net/libsigc2/docs/reference/html/classsigc_1_1signal2.html

At the moment we have, for instance:

template <class T_return, class T_arg1, class T_arg2, class T_accumulator = nil>
class signal2

If we make that variadic then we'd need to have the accumulator before the variadic parameters:

template <class T_return, class T_accumulator, class... T_arg>
class signal

but then it can't be optional.
Comment 14 Marcin Kolny (IRC: loganek) 2016-01-07 23:04:14 UTC
I think we should provide a variant of signal class where T_accumulator=nil. In most of the cases T_accumulator is not used, by the user. Moreover, he'll be confused if we force him to pass something there (even though it will be explained in the documentation).
As you said, we can't use optional parameter. So what about two different classes?
Comment 15 Murray Cumming 2016-01-09 19:16:51 UTC
Created attachment 318598 [details] [review]
0001-bind.h.m4-Try-to-make-some-operator-variadic.patch

I'm making pretty good progress. Now I have to make the adaptors, such as bind() and hide(), variadic. I think this will need to us to manipulate the parameter packs via tuples and then call the intended methods via those tuples. Here is a small patch (against the "variadic" branch) that tries to do that for a small part of bind(). But it doesn't build.

I suspect that I'll now have to convert much more of the code to variadic templates before it starts building again. Still, it would be nice if someone can see a problem with this patch already.
Comment 16 Murray Cumming 2016-01-09 22:55:11 UTC
In case anyone is looking at my problem with that last patch, nevermind for now. I'm working on a solution.
Comment 17 Murray Cumming 2016-01-10 11:07:50 UTC
I'm making good progress making bind() completely variadic, though it's not ready to commit yet.

I'm currently stuck on the need for a fully templated way to transform one tuple into another by operating on each element of the tuple.

This tuple_transform_each() works fine:
https://github.com/murraycu/murrayc-tuple-utils/commit/5c2747e52d0a0352eb2ee620720ca8172ee643de

But when I try to make that take a template template parameter (such as transformer instead of transformer<int>), a template specialization doesn't build:
https://github.com/murraycu/murrayc-tuple-utils/commit/e6b3189ae06a2002a6399d3b01097c852d8faa1e

I'd appreciate any help with that.
Comment 18 Marcin Kolny (IRC: loganek) 2016-01-10 15:20:00 UTC
I've sent a pull [1] request on a github. It fixes a build, but there is still segfault. I'll look at this a bit later. 

[1] https://github.com/murraycu/murrayc-tuple-utils/pull/1
Comment 19 Marcin Kolny (IRC: loganek) 2016-01-10 15:31:43 UTC
In the same pull request, I also fixed unittest (the reason of segfault mentioned in comment above was failing assert, I didn't noted your TODO before).
Comment 20 Murray Cumming 2016-01-10 22:01:28 UTC
Thanks. I'm now trying to do the same for the tuple type, rather than the tuple, and I'm struggling to use std::result_of(). Any ideas?
https://github.com/murraycu/murrayc-tuple-utils/blob/transform_each/tuple-utils/tuple_transform_each.h#L37

Here is the compiler error:
https://github.com/murraycu/murrayc-tuple-utils/commit/588147dd497d393ebeb8a5a1bc4f21b9aec66759
Comment 21 Marcin Kolny (IRC: loganek) 2016-01-10 22:35:48 UTC
There is a pull request: https://github.com/murraycu/murrayc-tuple-utils/pull/3
Comment 22 Murray Cumming 2016-01-13 12:35:11 UTC
Thanks.

Now I am stuck on creating a transform_each() that lets transform() call non-const methods on the original tuple elements. We need this so we can call bound_argument::invoke() on a tuple of bound_argument and keep the results (whose type depends on the particular invoke() return types) as well as keeping the invoke()ed bound_arguments.

The current implementation loses the changes because we copy the elements along the way and call transform() on those copies. I wonder if there's some std::move()/rvalue-reference cleverness we should use here.
https://github.com/murraycu/murrayc-tuple-utils/tree/transform_each_nonconst

(I've already split away a hacky transform_each_const<> to simplify the non-const code.)
Comment 23 Murray Cumming 2016-01-14 19:13:38 UTC
(In reply to Marcin Kolny (IRC: loganek) from comment #14)
> I think we should provide a variant of signal class where T_accumulator=nil.

There's now
  sigc::signal<result, arg1, arg2, argetc>
and
  sigc::signal<result, arg1, arg2, argetc>::accumulated<accumulator>
which is just another sigc::signal.

That syntax existed before but I guess it wasn't so useful until now.
Comment 24 Murray Cumming 2016-01-14 21:14:59 UTC
I'm making good progress in the variadic branch, though I've decide to leave bind() for later as it's the most complicated one. There are not many .m4 files left.

At the moment, I could make retype variadic if slot was variadic, but slot currently can't be variadic because of this:
https://git.gnome.org/browse/libsigc++2/tree/sigc++/functors/macros/slot.h.m4?h=variadic#n328

For some reason
  bound_functor*::operator()()
isn't being used when we try to call
  bound_functor*::operator()(a_...)
when sizeof...(a) is 0.
Comment 25 Marcin Kolny (IRC: loganek) 2016-01-14 23:15:54 UTC
Some time ago I've read about similar problem, but I don't remember, where, and what was the reason. I'll try to find it. Anyway, the solution is to define:
struct bind_functor<-1, T_functor, T_type1, nil, nil, nil, nil, nil, nil>::operator()() as follow:

  template <class T_DummyArg=void>
  decltype(auto)
  operator()()

Then, in the call_it(), we can skip this workaround, and use in all the variants(so in the varadic-based version as well) the same code:

  return (typed_rep->functor_).SIGC_WORKAROUND_OPERATOR_PARENTHESES<type_trait_take_t<T_arg>...>(a_...);
Comment 26 Murray Cumming 2016-01-15 09:14:16 UTC
Yes, thanks, that fixed it, and that let me get a bit further.

Right now I'm seeing if we can do without these const_*/volatile_*/const_volatile_* templates.
Comment 27 Murray Cumming 2016-01-15 10:14:20 UTC
Created attachment 319084 [details] [review]
0001-signal.h-Trying-to-use-std-result_of.patch

Can you also figure out the result_of<> syntax needed here to replace use of the result_type typedef?

This is one of the few places where that typedef is still used, and I hope we can make it unnecessary now that we have decltype(auto), and that might make several other traits templates unnecessary too.
Comment 28 Murray Cumming 2016-01-15 20:41:00 UTC
Created attachment 319150 [details] [review]
0001-bind-Try-to-make-this-variadic-instead-of-generated.patch

This is my attempt to make bind variadic.

However, it doesn't compile:

clangs says:

clang++ -DHAVE_CONFIG_H   -I.. -I..  -pedantic -Wall -Wextra -Wshadow -Wformat-security -Werror -Wall -g -O0 -std=c++14 -MT test_bind.o -MD -MP -MF .deps/test_bind.Tpo -c -o test_bind.o test_bind.cc
In file included from test_bind.cc:7:
../sigc++/adaptors/bind.h:171:27: error: no matching member function for call to 'operator()'
    return this->functor_.SIGC_WORKAROUND_OPERATOR_PARENTHESES<typename std::tuple_element<Is, T_specific>::type...>(std::get<Is>(tuple)...);
           ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../sigc++/adaptors/adaptor_trait.h:39:57: note: expanded from macro 'SIGC_WORKAROUND_OPERATOR_PARENTHESES'
  #define SIGC_WORKAROUND_OPERATOR_PARENTHESES template operator()
                                                        ^
../sigc++/adaptors/bind.h:149:14: note: in instantiation of function template specialization 'sigc::bind_functor<0, (anonymous namespace)::foo,
      int>::call_operator_parentheses_with_tuple<std::tuple<const int &, int &>, std::tuple<int, int>, 0, 1>' requested here
      return call_operator_parentheses_with_tuple<tuple_type_with_bound>(
             ^
test_bind.cc:100:48: note: in instantiation of function template specialization 'sigc::bind_functor<0, (anonymous namespace)::foo,
      int>::operator()<int>' requested here
  result_stream << sigc::bind<0>(foo(), -12345)(5);
                                               ^
../sigc++/adaptors/adaptor_trait.h:95:3: note: candidate function [with T_arg = <const int &, int &>] not viable: 2nd argument
      ('const __tuple_element_t<1UL, tuple<int, int> >' (aka 'const int')) would lose const qualifier
  operator()(T_arg... _A_arg) const


g++ says much the same thing:

g++ -DHAVE_CONFIG_H   -I.. -I..  -pedantic -Wall -Wextra -Wsuggest-override -Wshadow -Wzero-as-null-pointer-constant -Wformat-security -Werror -Wall -g -O0 -std=c++14 -MT test_bind.o -MD -MP -MF .deps/test_bind.Tpo -c -o test_bind.o test_bind.cc
In file included from test_bind.cc:7:0:
../sigc++/adaptors/bind.h: In instantiation of ‘decltype(auto) sigc::bind_functor<I_location, T_functor, T_bound>::call_operator_parentheses_with_tuple(const T&, std::index_sequence<Is ...>) [with T_specific = std::tuple<const int&, int&>; T = std::tuple<int, int>; long unsigned int ...Is = {0ul, 1ul}; int I_location = 0; T_functor = {anonymous}::foo; T_bound = {int}; std::index_sequence<Is ...> = std::integer_sequence<long unsigned int, 0ul, 1ul>]’:
../sigc++/adaptors/bind.h:149:73:   required from ‘decltype(auto) sigc::bind_functor<I_location, T_functor, T_bound>::operator()(T_arg ...) [with T_arg = {int}; int I_location = 0; T_functor = {anonymous}::foo; T_bound = {int}]’
test_bind.cc:100:50:   required from here
../sigc++/adaptors/bind.h:171:140: error: no matching function for call to ‘sigc::adaptor_functor<{anonymous}::foo>::operator()(std::__tuple_element_t<0ul, std::tuple<int, int> >&, std::__tuple_element_t<1ul, std::tuple<int, int> >&)’
     return this->functor_.SIGC_WORKAROUND_OPERATOR_PARENTHESES<typename std::tuple_element<Is, T_specific>::type...>(std::get<Is>(tuple)...);
                                                                                                                                            ^
In file included from ../sigc++/adaptors/bind.h:3:0,
                 from test_bind.cc:7:
../sigc++/adaptors/adaptor_trait.h:95:3: note: candidate: template<class ... T_arg> decltype(auto) sigc::adaptor_functor<T_functor>::operator()(T_arg ...) const [with T_arg = {T_arg ...}; T_functor = {anonymous}::foo]
   operator()(T_arg... _A_arg) const
   ^
../sigc++/adaptors/adaptor_trait.h:95:3: note:   template argument deduction/substitution failed:
In file included from test_bind.cc:7:0:
../sigc++/adaptors/bind.h:171:140: note:   cannot convert ‘std::get<1ul, {int, int}>((* & tuple))’ (type ‘std::__tuple_element_t<1ul, std::tuple<int, int> > {aka const int}’) to type ‘int&’
     return this->functor_.SIGC_WORKAROUND_OPERATOR_PARENTHESES<typename std::tuple_element<Is, T_specific>::type...>(std::get<Is>(tuple)...);
Comment 29 Marcin Kolny (IRC: loganek) 2016-01-15 20:46:11 UTC
Created attachment 319151 [details] [review]
automatic deduction accumulator's operator() return type

AFAIK, operator() always should have arguments of type (iterator, iterator). So why do you expect (T_args...) instead?
I'm attaching a patch.
Comment 30 Murray Cumming 2016-01-15 22:07:43 UTC
Comment on attachment 319151 [details] [review]
automatic deduction accumulator's operator() return type

Thanks.
Comment 31 Murray Cumming 2016-01-16 19:14:34 UTC
Comment on attachment 319150 [details] [review]
0001-bind-Try-to-make-this-variadic-instead-of-generated.patch

I got past that particular compilation error by passing the tuple to the call_*() helper method as non-const, as I have in other files. That doesn't seem quite right.

I've put the code in a new variadic_bind branch to make it easier to play with:
https://git.gnome.org/browse/libsigc++2/tree/sigc++/adaptors/bind.h?h=variadic_bind

At the moment, I see these compilation errors (from clang++):

In file included from test_bind.cc:7:
In file included from ../sigc++/adaptors/bind.h:8:
../sigc++/tuple_transform_each.h:134:16: error: call to deleted constructor of 'const (anonymous namespace)::book'
    const auto element = T_transformer<element_type>::transform(std::get<index>(t_original));
               ^         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../sigc++/tuple_transform_each.h:151:62: note: in instantiation of function template specialization 'sigc::(anonymous
      namespace)::tuple_transform_each_impl<TransformEachInvoker,
      0>::tuple_transform_each<std::tuple<sigc::bound_argument<std::reference_wrapper<(anonymous namespace)::book> > >,
      std::tuple<sigc::bound_argument<std::reference_wrapper<(anonymous namespace)::book> > > >' requested here
  return tuple_transform_each_impl<T_transformer, size - 1>::tuple_transform_each(t, t);
                                                             ^
../sigc++/adaptors/bind.h:138:22: note: in instantiation of function template specialization
      'sigc::tuple_transform_each<internal::TransformEachInvoker, std::tuple<sigc::bound_argument<std::reference_wrapper<(anonymous namespace)::book>
      > > >' requested here
      auto t_bound = tuple_transform_each<internal::TransformEachInvoker>(bound_);
                     ^
test_bind.cc:134:71: note: in instantiation of function template specialization 'sigc::bind_functor<0, std::__cxx11::basic_string<char> &((anonymous
      namespace)::book::*)(), std::reference_wrapper<(anonymous namespace)::book> >::operator()<>' requested here
  result_stream << sigc::bind<0>(&book::get_name, std::ref(test_book))();
                                                                      ^
test_bind.cc:76:3: note: 'book' has been explicitly marked deleted here
  book(const book&) = delete;
  ^
In file included from test_bind.cc:7:
../sigc++/adaptors/bind.h:205:22: error: no matching function for call to 'tuple_transform_each'
      auto t_bound = tuple_transform_each<internal::TransformEachInvoker>(bound_);
                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../sigc++/functors/slot.h:116:36: note: in instantiation of function template specialization 'sigc::bind_functor<-1, void
      (*)(std::__cxx11::basic_string<char> &), std::reference_wrapper<(anonymous namespace)::book> >::operator()<>' requested here
      return (typed_rep->functor_).SIGC_WORKAROUND_OPERATOR_PARENTHESES<type_trait_take_t<T_arg>...>
                                   ^
../sigc++/adaptors/adaptor_trait.h:39:57: note: expanded from macro 'SIGC_WORKAROUND_OPERATOR_PARENTHESES'
  #define SIGC_WORKAROUND_OPERATOR_PARENTHESES template operator()
                                                        ^
../sigc++/functors/slot.h:124:38: note: in instantiation of member function 'sigc::internal::slot_call<sigc::bind_functor<-1, void
      (*)(std::__cxx11::basic_string<char> &), std::reference_wrapper<(anonymous namespace)::book> >, void>::call_it' requested here
    { return reinterpret_cast<hook>(&call_it); }
                                     ^
../sigc++/functors/slot.h:191:84: note: in instantiation of member function 'sigc::internal::slot_call<sigc::bind_functor<-1, void
      (*)(std::__cxx11::basic_string<char> &), std::reference_wrapper<(anonymous namespace)::book> >, void>::address' requested here
      slot_base::rep_->call_ = internal::slot_call<T_functor, T_return, T_arg...>::address();
                                                                                   ^
test_bind.cc:153:10: note: in instantiation of function template specialization 'sigc::slot<void>::slot<sigc::bind_functor<-1, void
      (*)(std::__cxx11::basic_string<char> &), std::reference_wrapper<(anonymous namespace)::book> > >' requested here
    sl = sigc::bind(&egon, std::ref(guest_book));
         ^
../sigc++/tuple_transform_each.h:149:1: note: candidate template ignored: substitution failure [with T_transformer = internal::TransformEachInvoker, T
      = std::tuple<sigc::bound_argument<std::reference_wrapper<(anonymous namespace)::book> > >]
tuple_transform_each(T& t) {
^
Comment 32 Marcin Kolny (IRC: loganek) 2016-01-18 22:33:49 UTC
Created attachment 319311 [details] [review]
partial build fix

The problem was that ::transform method returned object, but not a reference to an object. I've fixed it in the patch, but there are still some errors.
Comment 33 Murray Cumming 2016-01-19 07:52:46 UTC
Thanks. I've pushed that to the variadic_bind branch. Please do feel free to commit to that branch.

Yes, we now have the following compiler error, somehow related to our use of std::ref() (previously sigc::ref(), though that's not the problem, I think. At this line:

  result_stream << sigc::bind<0>(&book::get_name, std::ref(test_book))();


In file included from test_bind.cc:7:
In file included from ../sigc++/adaptors/bind.h:5:
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/tuple:108:9: error: call to deleted constructor of
      '(anonymous namespace)::book'
      : _M_head_impl(__h) { }
        ^            ~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/tuple:357:9: note: in instantiation of member function 'std::_Head_base<0,
      (anonymous namespace)::book, false>::_Head_base' requested here
      : _Base(__head) { }
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/tuple:473:9: note: in instantiation of member function 'std::_Tuple_impl<0,
      (anonymous namespace)::book>::_Tuple_impl' requested here
      : _Inherited(__elements...) { }
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/tuple:970:14: note: in instantiation of member function
      'std::tuple<(anonymous namespace)::book>::tuple' requested here
      return __result_type(std::forward<_Elements>(__args)...);
             ^
../sigc++/tuple_transform_each.h:135:37: note: in instantiation of function template specialization 'std::make_tuple<const (anonymous namespace)::book
      &>' requested here
    const auto tuple_element = std::make_tuple(element);
                                    ^
../sigc++/tuple_transform_each.h:151:62: note: in instantiation of function template specialization 'sigc::(anonymous
      namespace)::tuple_transform_each_impl<TransformEachInvoker,
      0>::tuple_transform_each<std::tuple<sigc::bound_argument<std::reference_wrapper<(anonymous namespace)::book> > >,
      std::tuple<sigc::bound_argument<std::reference_wrapper<(anonymous namespace)::book> > > >' requested here
  return tuple_transform_each_impl<T_transformer, size - 1>::tuple_transform_each(t, t);
                                                             ^
../sigc++/adaptors/bind.h:137:22: note: in instantiation of function template specialization
      'sigc::tuple_transform_each<internal::TransformEachInvoker, std::tuple<sigc::bound_argument<std::reference_wrapper<(anonymous namespace)::book>
      > > >' requested here
      auto t_bound = tuple_transform_each<internal::TransformEachInvoker>(bound_);
                     ^
test_bind.cc:134:71: note: in instantiation of function template specialization 'sigc::bind_functor<0, std::__cxx11::basic_string<char> &((anonymous
      namespace)::book::*)(), std::reference_wrapper<(anonymous namespace)::book> >::operator()<>' requested here
  result_stream << sigc::bind<0>(&book::get_name, std::ref(test_book))();
                                                                      ^
test_bind.cc:76:3: note: 'book' has been explicitly marked deleted here
  book(const book&) = delete;
Comment 34 Murray Cumming 2016-01-19 08:15:51 UTC
I guess that we don't want to call invoke() until just before we pass the invoke() result to the operator()() method. But currently we pass invoke() and store the result along the way as we build the full tuple.
Comment 35 Murray Cumming 2016-02-11 21:27:35 UTC
I think we need to make sure that these tuple utilities work with std::ref() at all, like std::tuple_cat() does:
https://github.com/murraycu/murrayc-tuple-utils/blob/master/tests/test_tuple_cat.cc#L40

Starting with tuple_cdr(), I'm trying this in the std_ref branch of my murrayc-tuple-utils, in which this test fails:
https://github.com/murraycu/murrayc-tuple-utils/blob/std_ref/tests/test_tuple_cdr.cc#L79

Something must be storing a value in a tuple instead of a std::reference_wrapper<> or reference.
Comment 36 Murray Cumming 2016-02-19 12:13:05 UTC
Thanks to some helpful help from Jonathan Wakely, I reworked the various tuple utils, making them do perfect forwarding and avoiding std::make_tuple(), which tends to strip the std::reference_wrapper<> from std::ref()ed elements.

Now it actually builds:
https://git.gnome.org/browse/libsigc++2/log/?h=variadic_bind

One test fails at runtime:

$ ./test_functor_trait
   Test 1
Expected "hit all targets: other trackable int: 1 other "
Got      "hit all targets: other int: 1 trackable other "


That's this code:
https://git.gnome.org/browse/libsigc++2/tree/tests/test_functor_trait.cc?h=variadic_bind#n82

  int i = 1;
  int j = 2;
  int k = 3;
  A a;
  result_stream << "hit all targets: ";
  sigc::visit_each(print(), sigc::compose(sigc::bind(sigc::ptr_fun(&foo), std::ref(a), i), sigc::ptr_fun(&bar)));
  util->check_result(result_stream, "hit all targets: other trackable int: 1 other ");


Would anyone like to figure out what's happening there?
Comment 37 Murray Cumming 2016-02-19 21:07:51 UTC
Incidentally, what is sigc::compose() even meant to do? I can't make sense of the example in the documentation:
http://libsigc.sourceforge.net/libsigc2/docs/reference/html/group__compose.html#_details
Comment 38 Murray Cumming 2016-03-01 21:52:59 UTC
I'm now working in the variadic_bind2 branch.
Comment 39 Murray Cumming 2016-03-02 13:46:31 UTC
I've done lots of rebasing to clean up the history and to split up the changes to bind(). It's now here, in the variadic_bind3 branch:
https://git.gnome.org/browse/libsigc++2/log/?h=variadic_bind3

Now it always builds at every commit, which helps when investigating.

And I've found that tests_functor_traits first starts failing after this commit:
https://git.gnome.org/browse/libsigc++2/commit/?h=variadic_bind3&id=0e3253486a3e32d398c74d619b9ac900cf91c7d5
Comment 40 Murray Cumming 2016-03-02 21:27:20 UTC
That was caused by tuple_for_each() iterating in reverse. That's fixed:
https://github.com/murraycu/murrayc-tuple-utils/commit/3de113f0259e87550fb3cd3de6ef8ee5c53db7d4

I've rebased yet again. Now all the tests pass:
https://git.gnome.org/browse/libsigc++2/log/?h=variadic_bind4

I've also made it parallel-installable as sigc++-3.0 and I've tried it out in glibmm, where it seems to work:
https://git.gnome.org/browse/glibmm/log/?h=sigc3

Interestingly, sigc++-3.0 doesn't allow these incorrect uses of sigc<1>, which seems like a good thing:
https://git.gnome.org/browse/glibmm/commit/?h=sigc3&id=c66f3624d2de55512e82b5d5f3a29748a48fe91a
Comment 41 Murray Cumming 2016-03-02 21:37:52 UTC
There are still some small .m4 files to generate code. For instance, mem_fun.h needs to have various versions of mem_fun() to accept member method function pointers with the various (combinations of) modifiers such as const volatile, etc. And these return their own appropriately-named functors. For instance:

template <class T_return, class T_obj, class T_obj2, class... T_arg>
inline decltype(auto)
mem_fun(/*const*/ T_obj* _A_obj, T_return (T_obj2::*_A_func)(T_arg...) const volatile)
{ return bound_const_volatile_mem_functor<T_return, T_obj, T_arg...>(_A_obj, _A_func); }


It would be nice to avoid that.
Comment 42 Murray Cumming 2016-03-02 21:49:02 UTC
(In reply to Murray Cumming from comment #41)
> It would be nice to avoid that.

However, std::mem_fun() seems to need the same overloads:
http://en.cppreference.com/w/cpp/utility/functional/mem_fn
Comment 43 Murray Cumming 2016-03-02 21:56:26 UTC
(In reply to Murray Cumming from comment #42)
> (In reply to Murray Cumming from comment #41)
> > It would be nice to avoid that.
> 
> However, std::mem_fun() seems to need the same overloads:
> http://en.cppreference.com/w/cpp/utility/functional/mem_fn

However, those were removed in C++14. This strange signature seems to cover them instead:
mem_fn(R T::* pm)
Comment 44 Murray Cumming 2016-03-04 14:36:30 UTC
In the variadic_mem_fun2 branch I'm making progress on reducing the code generation for the remaining .m4 files. We could probably use them as plain .h files now, but I'd still like to make code like the following more generic instead of having the repetition.

It would be great if we could do some kind of type traits checking on the member function pointer type, to discover whether it is meant to be a const member method and/or a volatile member method. Then we could maybe just do some kind of:
  add_const_if<T_obj, member_method_is_const<func_type>::value>::type
.

template <class T_return, class T_obj, class... T_arg>
inline decltype(auto)
mem_fun(T_return (T_obj::*_A_func)(T_arg...) )
{ return mem_functor_base<
    T_return (T_obj::*)(T_arg...) ,
    T_obj,
    T_return, T_obj, T_arg...>(_A_func); }

template <class T_return, class T_obj, class... T_arg>
inline decltype(auto)
mem_fun(T_return (T_obj::*_A_func)(T_arg...) const)
{ return mem_functor_base<
    T_return (T_obj::*)(T_arg...) const,
    const T_obj,
    T_return, T_obj, T_arg...>(_A_func); }

template <class T_return, class T_obj, class... T_arg>
inline decltype(auto)
mem_fun(T_return (T_obj::*_A_func)(T_arg...) volatile)
{ return mem_functor_base<
    T_return (T_obj::*)(T_arg...) volatile,
    T_obj,
    T_return, T_obj, T_arg...>(_A_func); }

template <class T_return, class T_obj, class... T_arg>
inline decltype(auto)
mem_fun(T_return (T_obj::*_A_func)(T_arg...) const volatile)
{ return mem_functor_base<
    T_return (T_obj::*)(T_arg...) const volatile,
    const T_obj,
    T_return, T_obj, T_arg...>(_A_func); }
Comment 45 Murray Cumming 2016-03-05 23:16:22 UTC
I have now reduce it to this, and I don't think we can avoid specifying T_arg... separately. We could specify the const and volatile as bools instead of in a member function pointer, but that seems less clear.

template <class T_return, class T_obj, class... T_arg>
inline decltype(auto)
mem_fun(T_return (T_obj::*_A_func)(T_arg...) )
{ return mem_functor_base<
    T_return (T_obj::*)(T_arg...) ,
    T_arg...>(_A_func); }

template <class T_return, class T_obj, class... T_arg>
inline decltype(auto)
mem_fun(T_return (T_obj::*_A_func)(T_arg...) const)
{ return mem_functor_base<
    T_return (T_obj::*)(T_arg...) const,
    T_arg...>(_A_func); }

template <class T_return, class T_obj, class... T_arg>
inline decltype(auto)
mem_fun(T_return (T_obj::*_A_func)(T_arg...) volatile)
{ return mem_functor_base<
    T_return (T_obj::*)(T_arg...) volatile,
    T_arg...>(_A_func); }

template <class T_return, class T_obj, class... T_arg>
inline decltype(auto)
mem_fun(T_return (T_obj::*_A_func)(T_arg...) const volatile)
{ return mem_functor_base<
    T_return (T_obj::*)(T_arg...) const volatile,
    T_arg...>(_A_func); }
Comment 46 Murray Cumming 2016-03-07 10:28:51 UTC
This is all now in the master branch, with libsigc++-2.0 in the libsigc++-2-8 branch. I will soon do a libsigc++-3.0 release as libsigc++-2.99.1.

There are now no .m4 files, though mem_fun.h is still a bit repetitive.

Please do suggest improvements in separate bug reports.