2

以下を使用して、関数の引数の型を「剥がす」ことができます。

void foo_double(double a)
{
}

void foo_int(int a)
{

}

template <class R, class A0>
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,...,CnPython に関数として公開する必要がある複数のクラスがあります。void Run()すべてのクラスに共通のメソッドがあると仮定しましょう。ただし、これらのクラスのコンストラクターは異なる引数を受け入れます。関数を python に公開するには、boost.python を使用します。これは、すべての型変換 (主にプリミティブ) を処理しながら、関数を適切な python 関数に自動的にエクスポートします。

私の最初の解決策は次のとおりです。

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

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

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

template <class T, class A0>
void python_wrapper(A0 a0)
{
    T instance(a0);
    instance.Run();
}

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

そして...それは動作します。

私が今達成しようとしているのは、コンストラクターの引数の型を推測するpython_wrapper<C2>代わりに使用することです。python_wrapper<C2, double>

元の投稿で示したように。クラスの代わりに関数をラップしていれば、同様のことを達成できます。

4

4 に答える 4

9

型のコンストラクターの引数を推測する方法はありません。

C++98/03 および C++11 の仕様では、型推論が発生する可能性があるコンテキストを明示的にリストしています (§ 14.8.2 およびそのサブセクションを参照してください)。控除は、テンプレート プログラミングのライフ エンハンスメントの資格であり、必須ではありません。演繹によって実行できることはすべて、明示的な呼び出しによっても達成できます。したがって、演繹を可能にするためには、コンストラクターを関数テンプレートに明示的に提供できる必要があります。

ただし、これは不可能です。C++98/03 および C++11 仕様の § 12.1 に記載されているように、コンストラクターには名前がありません。さらに、C++98/03 の § 12.1.12 および C++11 の § 12.1.10 は、コンストラクターのアドレスを取得してはならないことを示しています。したがって、識別子を提供する方法はありません。したがって、控除は発生しません。


控除が不可能なため、別の解決策を検討する価値があるかもしれません。各ソリューションには独自の長所と短所がありますが、それらのすべてでは、引数の型をコンストラクターの外のコンテキストで明示的にリストする必要があります。

  • コンストラクターの引数の型を関数テンプレートに提供します。
    • 長所:
      • かなり単純です。
    • 短所:
      • 型とその型のコンストラクターの引数の型との間の関連付けはあまりわかりません。
      • 関連付けは再利用できません。たとえば、関連付けを複数の関数テンプレートまたはクラス テンプレートに渡す場合は、関連付けを複製する必要があります。
      • アリティが 1 以上のコンストラクタを持つすべての型に必要です。
  • 型特性との関連付けを維持します。
    • 長所:
      • 再利用可能。
      • 型とその型のコンストラクターの引数の型との間の関連付けがより明確になります。
      • 過度に複雑ではありません。
    • 短所:
      • コーディングは、関連付けを関数テンプレートに直接提供するよりも少し複雑です。
      • アリティが 1 以上のコンストラクタを持つすべての型に必要です。
  • タイプごとにファクトリー関数を作成します。
    • 長所:
      • とてもシンプルです。
      • 再利用可能。
      • 型とその型のコンストラクターの引数の型との間の関連付けは非常に明確です。
    • 短所:
      • アリティが 0 の場合でも、すべてのタイプに必要です。これは、スコープによるあいまいさがなくなるため、侵襲的なファクトリ関数で軽減できます。非侵襲的なファクトリ関数の場合、署名に衝突が発生する可能性があるため、関数名は一意である必要があります。
  • メタプログラミングを多用して、コンストラクターの引数の型のベクトルを設定します。テンプレート コードは、実行可能な一致を識別しようとして、増大するリストを反復処理します。
    • 長所:
      • 型に同様のコンストラクターがある場合、ベクター内の 1 つのエントリが複数の型の実行可能な一致として機能する可能性があります。
    • 短所:
      • はるかに複雑です。
      • テンプレートの深さをサポートするためにコンパイラ引数を変更する必要がある場合があります。

ご使用の環境で説明されている状況を考えると、次のようになります。

  • 多くのクラスがあります。
  • 一部はすでに存在し、変更すべきではありません。
  • これらの多くは日常的に書かれています。
  • コンストラクターは一意です。

C++ 仕様で因数分解すると、小林丸を定義したと思います。環境に適応できるアプローチを決定するには、長所と短所を比較検討する必要があります。最も単純なアプローチは、より多くの型が作成されたときにコードを変更するために必要な場所が 1 つだけであるため、既に用意されているものである可能性があります。


それにもかかわらず、非侵襲的な方法で型のコンストラクターに関する情報を提供する型特性を使用するアプローチを次に示します。可変個引数テンプレートなどの C++11 機能がなければ、定型コードが少しあります。さらに、複数のコンストラクターなど、実装がすべてのケースをカバーしているとは限りません。

元の質問で提示されたクラスを使用する:

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

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

コンストラクターの特性を表すテンプレートが使用されます。コンストラクターの引数の型を表すために、Boost.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<Foo>::type should be
///        boost::mpl::vector<int>.
template <typename T>
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<C2>
{
  typedef boost::mpl::vector<double> type;
};

コンストラクタのboost::mpl::vector引数を表す型のリストです。経由のランダム アクセスを提供しますboost::mpl::at。要素へのアクセスを少しきれいにするために、ヘルパー型が導入されています。

/// @brief Helper type that makes getting the constructor argument slightly
///        easier.
template <typename Vector,
          std::size_t Index>
struct get_constructor_arg
  : boost::mpl::at<Vector, boost::mpl::int_<Index> >
{};

関数を Boost.Python に公開する場合、望ましい構文は単一の型のみを提供することです。関数テンプレートまたはクラス テンプレートのいずれかを使用して、この問題を解決できます。定型コードの一部を削減するため、クラス テンプレートを使用することにしました。

/// @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 <typename T,
          typename Args = typename constructor_traits<T>::type,
          std::size_t = boost::mpl::size<Args>::value>
struct runner
{
  static void run()
  {
    T().Run();
  }
};

このテンプレートは、コンストラクターが受け入れる引数の量に特化しています。以下は、1 つの引数を受け入れるように特殊化されています。これは1、特殊化のテンプレート引数リスト内の によって決定されます。

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

関数テンプレートを使用して、この問題を解決することもできます。クラス テンプレートを使用することにした理由は次のとおりです。

  • SFINAEは必要ありません。enable_if正しいテンプレートを選択するには、構造を使用する必要があります。
  • クラステンプレートでデフォルトのテンプレート引数を提供できるため、constructor_trait複数回取得する必要がなくなります。

結果の Boost.Python 呼び出しは次のようになります。

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

完全なコードは次のとおりです。

#include <iostream>
#include <boost/mpl/vector.hpp>
#include <boost/python.hpp>

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<Foo>::type should be
///        boost::mpl::vector<int>.
template <typename T>
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<C2>
{
  typedef boost::mpl::vector<double> type;
};

/// @brief Helper type that makes getting the constructor argument slightly
///        easier.
template <typename Vector,
          std::size_t Index>
struct get_constructor_arg
  : boost::mpl::at<Vector, boost::mpl::int_<Index> >
{};

/// @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 <typename T,
          typename Args = typename constructor_traits<T>::type,
          std::size_t = boost::mpl::size<Args>::value>
struct runner
{
  static void run()
  {
    T().Run();
  }
};

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

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

そしてテスト出力:

>>> import example
>>> example.f1()
run c1
>>> example.f2(3.14)
run c2: 3.14
于 2013-01-10T21:17:03.180 に答える
0

コンストラクターのアドレスを取得することはできません (C++98 標準 12.1/12 コンストラクター - "12.1-12 コンストラクター - "コンストラクターのアドレスは取得されません。")

すべてのクラスが基本クラスを共有する場合、ファクトリを作成できますが、C++ 11 可変個引数テンプレートがないと、引数転送を行う方法がわかりません。

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


auto value = &construct<int>;

これで、呼び出されたときに type* を生成するバインド可能な関数ができました

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

さまざまなコンストラクター入力を可能にするために、これは C++11 を使用します

template <typename type>
class constructor {

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

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

使用法:

fooBarPtr a = constructor<fooBar>::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 <typename type>
std::shared_ptr<type> alloc(int argCount,...) {
    va_list list;
    va_start(list, argCount);
    auto result = std::shared_ptr<type>( new type(argCount,list) );
    va_end(list);
    return result;
}

使用法:

auto foo1 = alloc<fooy>(0); // passing zero args
auto foo2 = alloc<fooy>(1,1234); // passing one arg
于 2013-01-09T15:17:22.933 に答える
0

既存のクラスを変更したくないため、C++ 内でこれらのクラスの推定を自動化する方法はありません。ソース コードから署名情報を抽出することにより、これらのクラスの C++ の外部で実行できます。この抽出は、手動で行うことも、スクリプトや C++ プログラムによって自動化することもできます。

func既存のクラス E ごとに、抽出された情報を使用して、クラス E のコンストラクターに引数 (複数可) を渡し、メソッドを呼び出す静的メンバー関数を定義するクラス テンプレートの特殊化を定義しますRun

新しいクラスの場合は、単純に を定義するよう要求しfunc、クラス テンプレートのデフォルトで既存の を使用するようにしますfunc

次に、一般的な Python ラッパー定義コードで の引数を推測できfuncます。

これは、クラスを定義する人々がfunc新しいそのようなクラスの追加を開始するだけでよいことを意味します。


もう少し高いレベルでは、関連する人々を巻き込み、無数のクラスを定義することによって、良くない設計がどのように採用され、定着するかを調べることに時間を割く価値があると思います。将来、そのようなことが再び起こらないようにすることを目的としています。それをどのように処理するかは、それがどのように起こったかによって異なると思います。

于 2013-01-13T09:25:37.827 に答える
0

これが python::def とどのように相互作用するかはわかりませんが、一般的にこれは可変個引数テンプレートの目的です:

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

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

template<typename CLASS, typename... PARAMS>
void call(PARAMS... params) {
  CLASS inst(params...);
  inst.run();
}

main() {
  call<C1>();
  call<C2>(42);
}

これは c++11 ですが、バージョン 4.3 以降の gcc でサポートされています。

于 2013-01-14T19:21:08.223 に答える