home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   comp.lang.c++.moderated      Moderated discussion of C++ superhackery      33,346 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 31,992 of 33,346   
   =?ISO-8859-1?Q?Daniel_Kr=FCgler?= to All   
   Re: Haskell-style "dot" operator   
   07 Mar 12 00:55:15   
   
   24b42e21   
   From: daniel.kruegler@googlemail.com   
      
   Am 06.03.2012 22:18, schrieb Jimmy H.:   
   > I sought to define a Haskell-style "dot" operator for C++, which would   
   > be operator*.   
   >   
   > I wrote:   
   >   
   > #include   
   > #include   
   >   
   > template   
   > auto operator*(std::function  a, std::function  b) ->   
   >      std::function   
   > {   
   >      return [a,b](C c) { return a(b(c)); };   
   > }   
      
   Let me start with a initial warning here: In general I recommend to stay   
   away from adding operators for types outside of their own namespaces,   
   because these are only found under very specific and simple situations   
   as in this code. ADL won't work for example, because the global   
   namespace is no associated namespace of std::function objects.   
      
   > int addOne(int a) {   
   >      return a + 1;   
   > }   
   >   
   > int main() {   
   >      std::function  addOneFunc(addOne);   
   >      std::cout<<  (addOneFunc * addOneFunc)(2)<<  std::endl;   
   > }   
   >   
   > Notably, though, I need to explicitly convert addOne to a   
   > std::function for it to call the custom operator* function. Attempting   
   > to write a helper template to do the conversion for me:   
   >   
   > template   
   > auto operator*(A a, B b) ->   
   >      decltype(std::function(a) * std::function(b))   
   > {   
   >      return std::function(a) * std::function(b);   
   > }   
   >   
   > This does not help.   
      
   This won't work, because your arguments would never be deduced as   
   function types. Note that A and B in above signature would be required   
   to match a type pattern R(Args) which is a function type. What you   
   provide in the code will be a function pointer R(*)(Args) and that won't   
   result in successful instantiation of the std::function template.   
      
   > Is it simply impossible to add custom operators to   
   > built-in types? If that is the case, why can't I write:   
   >   
   > int main() {   
   >      std::function  addOneFunc(addOne);   
   >      std::cout<<  (addOneFunc * addOne)(2)<<  std::endl;   
   > }   
   >   
   > That has a std::function in it!   
      
   This argument is irrelevant: For any general conversion situation a   
   compiler cannot deduce for some function template the template parameter   
   types (thus B and C in std::function) when you provide any   
   argument (presumably a function pointer B(*)(C) in this example) that is   
   just *convertible* to the parameter type. Therefore function templates   
   do not consider any conversions when arguments are provided. The typical   
   solution out of this problem is to add overloads where either the first   
   or the second parameter is any valid argument type.   
      
   Now it would be rather simple to add one overload that accepts any   
   callable object on the *left* side of your operator*, thus referring to   
   an expression of the form   
      
   (addOne * addOneFunc)(2)   
      
   We just need to define some helper traits to validate that this first   
   argument can be called with a std::function, which essentially   
   means that we have determined the argument of the call. Let's write our   
   trait first:   
      
   #include    
   #include    
      
   template   
   struct callable_traits   
   {   
   private:   
     template   
     static auto test(int) ->   
   decltype(std::declval()(std::declval()...));   
      
     struct failure {};   
      
     template   
     static auto test(...) -> failure;   
      
   public:   
     typedef decltype(test(0)) result_type;   
      
     static const bool value = !std::is_same::value;   
   };   
      
   and let's add an additional operator* overload:   
      
   template    
   auto operator*(F a, std::function b) ->   
     typename std::enable_if::value,   
     std::function::result_type(C)>>::type   
   {   
     return [a, b](C c) { return a(b(c)); };   
   }   
      
   Notice, that we can deduce all necessary information from the arguments:   
   The previous return type A is just the return type of our function   
   object type F and the argument of the call is just the result of the   
   right std::function object, so it must be some value of type B.   
      
   Now try to define a third overload where we accept *any* function object   
   type on the right side. This will turn out to be a problem that does not   
   have a unique solution, because you would need to find out the necessary   
   argument type that makes a function-call valid given some return type.   
   It is thus not possible *for the general case* for the compiler to find   
   an unambiguous type C that makes the coupled function call well-formed.   
   In contrast to a function call expression which provides a mapping from   
   a given argument type to the return type there does not exist a general   
   expression that returns the argument type given some return type.   
      
   In your scenario a simpler solution works, if you accept to be   
   restricted to function pointers as argument types. In this case just write:   
      
   template    
   auto operator*(std::function a, B(*b)(C)) -> std::function   
   {   
     return [a, b](C c) { return a(b(c)); };   
   }   
      
   and your call expression   
      
   (addOneFunc * addOne)(2)   
      
   is deducible again. If you can live with this restriction, you can throw   
   away the initially suggested callable_traits and define your first   
   overload as follows:   
      
   template    
   auto operator*(A(*a)(B), std::function b) -> std::function   
   {   
     return [a, b](C c) { return a(b(c)); };   
   }   
      
   HTH & Greetings from Bremen,   
      
   Daniel Krügler   
      
      
   --   
         [ See http://www.gotw.ca/resources/clcm.htm for info about ]   
         [ comp.lang.c++.moderated.    First time posters: Do this! ]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca