Вывод аргумента шаблона аргументами конструктора

Возможно "очистить" использование типа аргумента функции:

void foo_double(double a)
{
}

void foo_int(int a)
{

}

template 
void bar(R (*fp)(A0))
{   
   //Do something related with A0
}

int main()
{
    bar(foo_double);//A0 is double
    bar(foo_int);   //A0 is int
}    

Действительно ли возможно сделать то же самое "очищение типа аргумента" к функции конструктора класса?

Править:

Я полагаю, что не объяснился ясно в оригинальном фрагменте кода. Вот полный сценарий.

У меня есть многократные классы C1..., Cn , который я должен выставить питону как функции. Позволяет предполагают, что у всех классов есть общее недействительный Пробег() метод. Однако конструкторы этих классов принимают различные аргументы. Чтобы выставить функции питону, я использую boost.python, который автоматически экспортирует функцию в соответствующую функцию питона, обращаясь со всеми преобразованиями типов (главным образом примитивы).

Мое первое решение было:

class C1 
{
public:
    C1() {}
    void Run();
};

class C2
{
public:
    C2(double a) {}
    void Run();
};

template 
void python_wrapper()
{
    T instance();
    instance.Run();
}

template 
void python_wrapper(A0 a0)
{
    T instance(a0);
    instance.Run();
}

BOOST_PYTHON_MODULE(_pythonmodule)
{
   //This is boost.python stuff
    python::def("f1", python_wrapper);
    python::def("f2", python_wrapper);
}

И... Это работает.

What I am trying to accomplish now is to use python_wrapper instead of python_wrapper while inferring the constructor argument types.

Поскольку я показал на оригинальной почте. Я мог достигнуть чего-то подобного, если бы я обертывал функции вместо классов.

2
добавлено отредактировано
Просмотры: 2
nl ja de
@R.MartinhoFernandes, я хочу произвести функцию бар (чтобы быть экспортированным в питона), который создает случай класса, вызывая конструктора. Явно не определяя c' аргументы скалистой вершины. Есть замкнутое множество классов, которые я использовал бы. Я don' t wan' t, чтобы изменить любого из них с этой целью.
добавлено автор Xyand, источник
@KerrekSB, Спасибо. Я думал об использовании его, но couldn' t предлагают решение.
добавлено автор Xyand, источник
@VaughnCato, бар (arg1, arg2)
добавлено автор Xyand, источник
Конструкторы don' у t есть имена и can' t быть названным как обычные функции членства. Я думаю станд.:: is_constructible может быть полезной чертой для любой проблемы you' ре, пытающееся решить.
добавлено автор Kerrek SB, источник
Нет. Почему вы хотели бы это? Знание типов аргумента функции главным образом безопасно, мм, я имею в виду, главным образом бесполезный. 99% времени, которое вы хотите знать, подлежащая ли функция выкупу определенным способом, не, если у аргументов есть определенный тип.
добавлено автор R. Martinho Fernandes, источник
Как это <�закодировало> бы бар функция быть названным от питона? бар (MyClass, arg1, arg2) ?
добавлено автор Vaughn Cato, источник
Что, если есть многократные конструкторы для класса? Без odr-использования, как компилятор знал бы который перегрузки иллюстрировать примерами?
добавлено автор Joel Cornett, источник

4 ответы

Нет никакого способа вывести аргументы конструктора типа.

C++ 98/03 и C++, 11 технических требований явно перечисляют контекст, в котором может произойти вычет типа (относятся к § 14.8.2 и его подразделам). Вычет - квалифицирование жизненного улучшения для шаблонного программирования, и не обязательный. Что-либо, что может быть сделано посредством вычета, может также быть достигнуто посредством явных требований. Таким образом, для вычета, чтобы быть возможным, это потребовало бы, чтобы можно было явно предоставить конструктора шаблону функции.

Однако это невозможно. Как отмечено в § 12.1 из C++ 98/03 и C++ 11 технических требований, у конструкторов нет имен. Кроме того, § 12.1.12 из C++ 98/03 и § 12.1.10 из C++ 11 государств, что адрес конструктора не должен быть взят. Таким образом нет никакого способа обеспечить идентификатор; поэтому, вычет не может произойти.


С вычетом, являющимся невозможным, это могут быть альтернативные решения достойные рассмотрения. У каждого решения будут свои собственные наборы за и против, но все они потребуют, чтобы типы аргумента были явно перечислены в некотором контексте за пределами конструктора:

  • Provide the constructor's argument types to a function template.
    • Pros:
      • Fairly simple.
    • Cons:
      • The association between the type and the type's constructor's argument types is not very visible.
      • The association is not reusable. For example, the association would need to be duplicated if passing it to multiple function templates or class templates.
      • Required for every type that has a constructor with an arity of 1 or greater.
  • Maintain the association with a type trait.
    • Pros:
      • Reusable.
      • The association between the type and the type's constructor's argument types is more visible.
      • Not overly complicated.
    • Cons:
      • Coding is slightly more complex than providing the association directly to a function template.
      • Required for every type that has a constructor with an arity of 1 or greater.
  • Create a factory function for each type.
    • Pros:
      • Very simple.
      • Reusable.
      • The association between the type and the type's constructor's argument types is very visible.
    • Cons:
      • Required for every type, even if the arity is 0. This could be mitigated with an invasive factory functions, as there will be no ambiguities due to scope. For a non-invasive factory functions, there may be collisions on signatures, so the function names must be unique.
  • Heavy use of meta-programming to have a vector of constructor argument types. Template code would then iterate over the growing list trying to identify a workable match.
    • Pros:
      • If types have similar constructors, then a single entry in the vector may serve as a workable match for multiple types.
    • Cons:
      • Much more complex.
      • May require modifying compiler arguments to support template depth.

Учитывая ситуацию, описанную для вашей среды:

  • Там - много классов.
  • Некоторые уже существуют и не должны быть изменены.
  • Больше из них написан ежедневно.
  • конструкторы уникален.

Когда учтено со спецификацией C++, я полагаю, что мы определили a Кобаяши Мэру. Необходимо будет нагрузить за и против, чтобы определить, какой подход может быть адаптирован к среде. Самый простой подход может уже быть тем, который вы имеете в распоряжении, поскольку это только требует, чтобы единственное местоположение для кода изменилось, поскольку больше типов создается.


Тем не менее, вот подход, используя черту типа, которая предоставляет информацию о конструкторе типа неразрушающим способом. Без C++ 11 возможностей, таких как шаблоны variadic, есть немного шаблонного кода. Кроме того, внедрение может не покрыть все случаи, такие как многократные конструкторы.

Используя классы, представленные в оригинальном вопросе:

class C1 
{
public:
  C1();
  void Run();
};

class C2
{
public:
  C2(double a);
  void Run();
};

Будет использоваться шаблон, который представляет черты конструктора. Я использую список типов, предоставленный Повышение. MPL представлять типы аргумента конструктора. Дефолт constructor_traits указывает, что никакие аргументы не требуются.

/// @brief constructor_traits is a type_trait that is used to noninvasively
///        associated T with constructor meta information, such as T'
///        constructor's argument types.
///
///        For example, if Foo has a constructor that accepts a single
///        integer, then constructor_traits::type should be
///        boost::mpl::vector.
template 
struct constructor_traits
{
  typedef boost::mpl::vector<> type;
};

Эта черта тогда специализирована для типов с конструкторами, которые принимают аргументы, такие как C2 .

/// Specialize constructor_traits for C2 to indicate that its constructor
/// accepts a double.
template <>
struct constructor_traits
{
  typedef boost::mpl::vector type;
};

повышение:: mpl:: вектор список типов, который представляет аргументы конструктора. Это обеспечивает произвольный доступ через повышение:: mpl:: в. Чтобы обеспечить немного более чистый доступ к элементам, тип помощника вводится:

/// @brief Helper type that makes getting the constructor argument slightly
///        easier.
template 
struct get_constructor_arg
  : boost::mpl::at >
{};

Выставляя функции, чтобы Повысить. Питон, желаемый синтаксис должен только обеспечить единственный тип. Или шаблоны функций или шаблоны классов могут использоваться, чтобы решить эту проблему. Я решил использовать шаблоны классов, поскольку это уменьшает часть шаблонного кода.

/// @brief runner type is used to provide a static run function that
///        will delegate the construction and running of type T based
///        on T's constructor_traits.
template ::type,
          std::size_t = boost::mpl::size::value>
struct runner
{
  static void run()
  {
    T().Run();
  }
};

Этот шаблон тогда специализирован для суммы аргументов, которые принимает конструктор. Следующее специализировано, чтобы принять один аргумент. Это определяется 1 в списке аргумента шаблона специализации.

/// Specialization for runner for types with have a single argument
/// constructor.
template 
struct runner
{
  static void run(typename get_constructor_arg::type a0)
  {
    T(a0).Run();
  }
};

Шаблоны функций могли также использоваться, чтобы решить эту проблему. Я решил использовать шаблоны классов потому что:

  • Никакая потребность в SFINAE. Должен был бы использовать enable_if конструкции, чтобы выбрать правильный шаблон.
  • Способность предоставить аргументам шаблона по умолчанию шаблон класса предотвращает потребность получить constructor_trait многократно.

Получающееся Повышение. Требования питона были бы похожи:

BOOST_PYTHON_MODULE(_pythonmodule)
{
  boost::python::def("f1", &runner::run);
  boost::python::def("f2", &runner::run);
}

Вот полный код:

#include 
#include 
#include 

class C1 
{
public:
  C1() {}
  void Run() { std::cout << "run c1" << std::endl; }
};

class C2
{
public:
  C2(double a) : a_(a) {}
  void Run() { std::cout << "run c2: " << a_ << std::endl;}
private:
  double a_;
};

/// @brief constructor_traits is a type_trait that is used to noninvasively
///        associated T with constructor meta information, such as T'
///        constructor's argument types.
///
///        For example, if Foo has a constructor that accepts a single
///        integer, then constructor_traits::type should be
///        boost::mpl::vector.
template 
struct constructor_traits
{
  typedef boost::mpl::vector<> type;
};

/// Specialize constructor_traits for C2 to indicate that its constructor
/// accepts a double.
template <>
struct constructor_traits
{
  typedef boost::mpl::vector type;
};

/// @brief Helper type that makes getting the constructor argument slightly
///        easier.
template 
struct get_constructor_arg
  : boost::mpl::at >
{};

/// @brief runner type is used to provide a static run function that
///        will delegate the construction and running of type T based
///        on T's constructor_traits.
template ::type,
          std::size_t = boost::mpl::size::value>
struct runner
{
  static void run()
  {
    T().Run();
  }
};

/// Specialization for runner for types with have a single argument
/// constructor.
template 
struct runner
{
  static void run(typename get_constructor_arg::type a0)
  {
    T(a0).Run();
  }
};

BOOST_PYTHON_MODULE(example)
{
  boost::python::def("f1", &runner::run);
  boost::python::def("f2", &runner::run);
}

И тест произвел:

>>> import example
>>> example.f1()
run c1
>>> example.f2(3.14)
run c2: 3.14
8
добавлено
Спасибо. Но тогда я должен специализироваться constructor_traits для каждого класса, который я хочу обернуть (у меня есть многие). Как это отличается, чем мое решение? Все еще необходимо определить c' типы аргумента скалистой вершины, только в другом месте.
добавлено автор Xyand, источник
В данный момент они уникальны. Но больше из них написано ежедневно (даже временные классы / испытательные классы). Я имею мало контроля над теми c' скалистые вершины. Поскольку я подозревал, что ответ "нет, Вы can' t". Это удивляет меня, поскольку это может быть легко сделано для функций. Я узнал из вашего ответа, но в данный момент я думаю I' ll придерживаются моего решения. Это было бы более прямым для пользователей.
добавлено автор Xyand, источник
@Albert: Это расцепляет информацию от своего использования, и ассоциация типа конструктору может быть немного более видима, чем . Как несколько плакатов указали, там вероятно не техника, которая позволит вам, точный вычет как адрес и тип конструктора не доступен. У ваших многих классов есть подобные конструкторы? Был бы, ведя список информации о типе конструктора, и наличие шаблонов повторяет по попытке каждого быть осуществимым решением? Или уникально большинство конструкторов?
добавлено автор Tanner Sansbury, источник

Я не уверен, как это будет взаимодействовать с питоном:: определение, но в целом это - то, для чего variadic шаблоны:

struct C1{
  C1(){}
  void run(){}
};

struct C2{
  C2(int){}
  void run(){}
};

template
void call(PARAMS... params) {
  CLASS inst(params...);
  inst.run();
}

main() {
  call();
  call(42);
}

Это - C++ 11, но поддержанный в gcc начиная с версии 4.3

0
добавлено
Спасибо. Две проблемы. Я использую VS2012. Я должен выставить интерфейс функции питону - чтобы не назвать его.
добавлено автор Xyand, источник
Я haven' t использовал VS2012, но I' ve прочитан то, что у этого есть variadoc поддержка шаблона, если вы щипаете урегулирование. Я don' t знают что питон:: определение на самом деле. Вы, возможно, должны были бы явно специализировать требование там (но по крайней мере вы don' t должен написать требование многократно),
добавлено автор dspeyer, источник
@dspeyer: Я полагаю, что Альберт хочет решение, которое не требует constructor' s argument' s печатает, чтобы быть явно обеспеченным кроме в конструкторе самостоятельно. Например, <�у кода>, у &call есть тип пустота (*)() и &call , есть тип пустота (*) (интервал) , с требование шаблон функции, выводящий типы аргумента, базирующиеся только на обеспеченном классе во время экземпляра, не просьбе.
добавлено автор Tanner Sansbury, источник

Так как вы не хотите изменять существующие классы, нет никакого пути к <�ним>, автоматизируют вычет за те классы в C++. Можно сделать это вне C++ для тех классов, извлекая информацию о подписи из исходного кода. Это извлечение может быть сделано вручную или автоматизировано сценарием или даже программой C++.

Для каждого существующего класса E используйте извлеченную информацию, чтобы определить специализацию шаблона класса, который определяет статическую функцию членства func , который передает его аргумент (аргументы), если таковые имеются, конструктору класса E и призывает Пробег метод.

Для новых классов просто потребуйте, чтобы они определили func и позволяют дефолту шаблона класса к использованию существующего func .

Тогда код определения обертки генерала Пайтона может вывести аргументы в пользу func , который вы показали, что знаете, как сделать.

Это означает людей, которые определяют классы, просто должен начать добавлять func для нового такие классы.


В немного более высоком уровне я думаю, что стоило бы использовать некоторое время при вовлечении соответствующих людей и обнаружении, как нехороший дизайн мог быть принят и укреплен, определив огромное количество классов. С целью предотвращения этого снова в будущем. То, как иметь дело с этим, зависело бы, я думаю, о том, как это произошло.

0
добавлено

Вы не можете взять адрес конструктора (C++, 98 Стандартов 12.1/12 Конструкторы - "12.1-12 Конструкторов - "Адрес конструктора не должны быть взяты".)

Если все классы разделяют базовый класс тогда, вы могли бы сделать фабрику, но без C++ 11 variadic шаблонов я не знаю о способе сделать отправление аргумента.

template 
static type*  construct(){
    return new type();
}


auto value = &construct;

У вас теперь есть bindable функция, что, когда названо, производит тип*

auto value = &construct;
int* newInt = (*value)();
delete newInt;

Чтобы допускать переменные исходные данные конструкторов, это использует C++ 11

template 
class constructor {

public:
    template
    static std::shared_ptr  alloc(Args&& ...args){
        return std::shared_ptr(new type(std::forward(args)...));
    }
   //placement new
    template
    static std::shared_ptr  realloc( std::shared_ptr& object,Args&& ...args){
        (new (&(*object)) type(std::forward(args)...));
        return object;
     }
};

class fooBar{
public:
    fooBar(int x,float f){
    }
};
typedef std::shared_ptr fooBarPtr;

Использование:

fooBarPtr a = constructor::alloc(5,0.0f);

Или это могло бы быть ближе к тому, что хочет op.

class fooy{
    int _x = 0;
public:
    fooy(int argCount, va_list& list){
        if( argCount > 0){
            _x = va_arg(list, int);
        }
    }
};

template 
std::shared_ptr alloc(int argCount,...) {
    va_list list;
    va_start(list, argCount);
    auto result = std::shared_ptr( new type(argCount,list) );
    va_end(list);
    return result;
}

Использование:

auto foo1 = alloc(0);//passing zero args
auto foo2 = alloc(1,1234);//passing one arg
0
добавлено
@Nico, Едва ли. Класс fooy уже существует и shouldn' t быть измененными (есть много классов).
добавлено автор Xyand, источник
@Nico, Если я понимаю правильно тогда его doesn' t отвечают на мой вопрос. Я хотел бы немного использовать типы аргументов интервал, плавание в вашем примере, не делая явного звонка как alloc (5,0.0f)
добавлено автор Xyand, источник
I' m использующий VS2012. Никакие variadic шаблоны все же...
добавлено автор Xyand, источник
@Nice: Это didn' t, когда я добавил тот комментарий, это была просто первая связка кода и ничего иного
добавлено автор K-ballo, источник
Нисколько плохо, но это doesn' t объясняют что-либо... Как это, как предполагается, работает?
добавлено автор K-ballo, источник
I' ll счастливо +1 ваш ответ, если вы расширяете его, чтобы допускать местоположение через размещение новое, а не динамическое распределение!
добавлено автор Agentlien, источник
@Albert я предполагаю I' m просто потерянный относительно того, что вы пытаетесь сделать. Можете вы предоставили желаемому варианту использования код sudo?
добавлено автор Nico, источник
@Albert второе половина ближе к тому, что вы просите делать?
добавлено автор Nico, источник
@Agentlien я думаю that' s, что вы хотели. Я don' у t есть время, чтобы пойти, проверяют его ~!
добавлено автор Nico, источник
Делает это не предоставляет достаточно информации, чтобы ответить: "Действительно ли возможно сделать то же самое "очищение типа аргумента" к функции конструктора класса?" На чем еще я должен подробно остановиться?
добавлено автор Nico, источник