С++ 11, как проксировать функцию класса, имеющую только свое имя и родительский класс?

Интересно, можно ли с помощью boost::mpl/preprocessor или некоторых функций noce C++11 создать прокси-функцию из типа класса и имени функции.

Скажем, у нас было:

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

внутри класса Электронная почта. Мы знаем, что есть функция set_email, мы хотим создать прокси-класс с API, например

PROXY(Email, set_email, MyEmail)

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

и иметь возможность вызывать любую из перегрузок set_email. Возможно ли и как создать такой класс, который будет проксировать любое количество функций перегрузки, не зная их типов (только имена)?


person myWallJSON    schedule 10.12.2012    source источник
comment
Похоже, идеальная работа для вариативных шаблонов и идеальной переадресации, хотя необходимость сомнительна.   -  person Xeo    schedule 10.12.2012
comment
Обратите внимание, что Xeo не просто бросается в глаза своими словами — ключевые слова действительно являются вариативными шаблонами и идеальной пересылкой — обе функции C++ 11.   -  person Arafangion    schedule 10.12.2012


Ответы (1)


Как насчет этого:

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)...);                                                                      \
      }                                                                                                                         \
                                                                                                                                \
  };

Это компилируется и отлично работает для меня на 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);
}

Изменить (уменьшенная версия благодаря комментариям)

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)...));  \
      }                                                              \
  };
person Drax    schedule 14.12.2012
comment
Да, это работает... единственное, о чем я сейчас думаю, так это о том, как получить список всех классов функции (в том числе унаследованные) в автоматическом режиме? - person myWallJSON; 15.12.2012
comment
@myWallJSON Я не знаю достаточно красивого способа сделать это. Одна из лучших идей, которые я видел, — это использовать макросы boost::fusion, такие как BOOST_FUSION_ADAPT_STRUCT, а также этот вопрос и предоставленные ответы могут привести вас к чему-то полезному. - person Drax; 15.12.2012
comment
Законно возвращать void из функции void, поэтому я не думаю, что есть необходимость в особом случае. void foo() {}; void bar() { return foo(); } действителен (поддерживается именно для того типа кода, который вы написали). - person Luc Touraille; 21.02.2013
comment
@LucTouraille Я этого не знал, спасибо! Я отредактировал ответ с уменьшенной версией. - person Drax; 21.02.2013
comment
Есть ли штраф за использование Args&&... и std::forward... во время выполнения или все это делается во время компиляции? - person vk-code; 20.03.2019
comment
@vk-code многоточие ... и оценка rvalue/lvalue - это время компиляции. Единственное, что происходит во время выполнения, — это более или менее просто копирование адреса для каждого пересылаемого параметра, который обычно оптимизируется в этом простом сценарии. - person Drax; 21.03.2019
comment
спасибо @Drax, в документации на С++ четко указано, какие функции компилируются, а какие - во время выполнения? если да, то где я могу найти эту документацию? - person vk-code; 21.03.2019
comment
@vk-code Вам придется искать функцию за функцией, так как c++ — довольно сложный язык, хорошая часто используемая документация: en.cppreference.com - person Drax; 21.03.2019