C++11 how to proxy class function having only its name and parent class?
Asked Answered
I

1

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)?

Intermarriage answered 10/12, 2012 at 11:53 Comment(2)
Seems like a perfect job for variadic templates and perfect forwarding, although the need is questionable.Halvorson
Note, that Xeo isn't just being flamboyant with his words - the keywords are indeed "variadic templates", and "perfect forwarding" - both C++11 features.Fin
L
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)...));  \
      }                                                              \
  };
Leija answered 14/12, 2012 at 19:28 Comment(8)
Yes it works... only thing I wonder about now is how to get list of all class functions (including inherited) in automated manner?Intermarriage
@Intermarriage 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's macros like BOOST_FUSION_ADAPT_STRUCT, also this question and the provided answers might guide you to something usefull.Leija
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).Mendacity
@LucTouraille I didn't know that, thanks ! I edited the answer with a smaller version.Leija
Is there a runtime penalty of using Args&&... and std::forward... or all this is done compile time?Callery
@Callery 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.Leija
thanks @Leija , does c++ documentation clearly mentions which features are compile and which are runtime? if so where can I find that documentation?Callery
@Callery You'll have to look feature by feature as c++ is quite a complex language, a good commonly used documentation is: en.cppreference.comLeija

© 2022 - 2024 — McMap. All rights reserved.