4

I wonder if it is possible using boost::mpl/preprocessor or some noce C++11 features to create function proxy from class type and function name.

Say we had:

  inline void set_email(const ::std::string& value);
  inline void set_email(const char* value);

inside class Email. We know there is set_email function n it, we want to create a prox class with API like

PROXY(Email, set_email, MyEmail)

Email * email = new Email();
MyEmail * myEmail = new MyEmail(email);

and have abilety to call any of set_email overloads.Is it possible and how to create such class that would proxy any number of overload functions not knowing there types (only names)?

myWallJSON
  • 9,110
  • 22
  • 78
  • 149
  • 5
    Seems like a perfect job for variadic templates and perfect forwarding, although the need is questionable. – Xeo Dec 10 '12 at 11:55
  • 3
    Note, that Xeo isn't just being flamboyant with his words - the keywords are indeed "variadic templates", and "perfect forwarding" - both C++11 features. – Arafangion Dec 10 '12 at 13:06

1 Answers1

5

How about this:

proxy_macro.hpp

#include <type_traits>
#include <utility>

#define PROXY(proxified, member_function, proxy_name)                                                                           \
  class proxy_name                                                                                                              \
  {                                                                                                                             \
  private:                                                                                                                      \
    proxified & ref_;                                                                                                           \
                                                                                                                                \
  public:                                                                                                                       \
    proxy_name(proxified &ref)                                                                                                  \
      : ref_(ref)                                                                                                               \
      {                                                                                                                         \
      }                                                                                                                         \
                                                                                                                                \
    /* general version */                                                                                                       \
    template<typename ... Args>                                                                                                 \
    auto member_function(Args&& ... args)                                                                                       \
    -> typename std::enable_if<!std::is_void<decltype(ref_.member_function(std::forward<Args>(args)...))>::value,               \
                               decltype(ref_.member_function(std::forward<Args>(args)...))>::type                               \
      {                                                                                                                         \
        return (ref_.member_function(std::forward<Args>(args)...));                                                             \
      }                                                                                                                         \
                                                                                                                                \
    /* void return type version */                                                                                              \
    template<typename ... Args>                                                                                                 \
    auto member_function(Args&& ... args)                                                                                       \
    -> typename std::enable_if<std::is_void<decltype(ref_.member_function(std::forward<Args>(args)...))>::value,                \
                               void>::type                                                                                      \
      {                                                                                                                         \
        ref_.member_function(std::forward<Args>(args)...);                                                                      \
      }                                                                                                                         \
                                                                                                                                \
  };

This compiles and work fine for me on g++ 4.7:

#include "proxy_macro.hpp"

#include <iostream>
#include <string>

class   Email
{
public:  
  void set_email(const ::std::string& value)
  {
    std::cout << value << std::endl;
  }

  void set_email(const char* value)
  {
    std::cout << value << std::endl;
  }

  int   set_email()
  {
    return (42);
  }

};

PROXY(Email, set_email, MyEmail)

int main(void)
{
  Email   mail;
  MyEmail my_mail(mail);

  std::string str = "test string";
  const char * ptr = "test char pointer";

  my_mail.set_email(str);
  my_mail.set_email(ptr);

  std::cout << "test return: " << my_mail.set_email() << std::endl;

  return (0);
}

Edit (smaller version thanks to comments)

proxy_macro.hpp

#include <type_traits>
#include <utility>

#define PROXY(proxified, member_function, proxy_name)                \
  class proxy_name                                                   \
  {                                                                  \
  private:                                                           \
    proxified & ref_;                                                \
                                                                     \
  public:                                                            \
    proxy_name(proxified &ref)                                       \
      : ref_(ref)                                                    \
      {                                                              \
      }                                                              \
                                                                     \
    template<typename ... Args>                                      \
    auto member_function(Args&& ... args)                            \
    -> decltype(ref_.member_function(std::forward<Args>(args)...))   \
      {                                                              \
        return (ref_.member_function(std::forward<Args>(args)...));  \
      }                                                              \
  };
Drax
  • 12,682
  • 7
  • 45
  • 85
  • [Yes it works](http://liveworkspace.org/code/KKkbu$2)... only thing I wonder about now is how to get list of all class functions (including inherited) in automated manner? – myWallJSON Dec 15 '12 at 16:17
  • @myWallJSON I am not aware of a "pretty enough" way to do that. One of the best idea i've seen is to use [boost::fusion](http://www.boost.org/doc/libs/1_52_0/libs/fusion/doc/html/index.html)'s macros like [BOOST_FUSION_ADAPT_STRUCT](http://www.boost.org/doc/libs/1_52_0/libs/fusion/doc/html/fusion/adapted/adapt_struct.html), also [this question and the provided answers](http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence) might guide you to something usefull. – Drax Dec 15 '12 at 18:51
  • 2
    It is legal to return `void` from a `void` function, so I don't think there is a need for the special case. `void foo() {}; void bar() { return foo(); }` is valid (it is supported exactly for the kind of code you wrote). – Luc Touraille Feb 21 '13 at 11:33
  • @LucTouraille I didn't know that, thanks ! I edited the answer with a smaller version. – Drax Feb 21 '13 at 19:18
  • Is there a runtime penalty of using `Args&&... ` and `std::forward...` or all this is done compile time? – vk-code Mar 20 '19 at 18:30
  • @vk-code the `...` elipsis and the rvalue/lvalue evaluation is compile time. The only thins happening at runtime is more or less just copying an address for each forwarded parameter which usually is optimized away in this simple scenario. – Drax Mar 21 '19 at 13:40
  • thanks @Drax , does c++ documentation clearly mentions which features are compile and which are runtime? if so where can I find that documentation? – vk-code Mar 21 '19 at 14:26
  • @vk-code You'll have to look feature by feature as c++ is quite a complex language, a good commonly used documentation is: https://en.cppreference.com/ – Drax Mar 21 '19 at 17:18