2

複雑な偏微分方程式系を解くときの作業を少し楽にするために、C 数値ライブラリ (の関連部分) の周りに C++ ラッパーを作成しています。複数の未知数を扱う場合、ライブラリは配列を各グリッド ポイントに割り当て、そのポインターをユーザー指定の関数に渡します。ユーザーは、F[0]、F[1]、... を介してそれぞれの未知数を参照できます。

もちろん、これらの未知のものには通常、適切な数学的な名前が付けられており、そのように参照できると便利です。struct明らかな解決策は、 likeを定義することです

template <typename T>
struct unknowns
{
    T a;
    T b;
    T c;
    T d;
};

double*unknowns<double>*usingに変換しreinterpret_castます。これは機能しているように見えますが、読んだ後、配列のように構造体を扱うことができますか? 私は、この変換プロセスの正確性を保証するより良い解決策を見つけようとしています (また、非スカラーを適切に処理しTます。これは後で必要になる可能性があります)。

次に、2 番目に明白な解決策は、代わりにvars成立するように再定義し、次のようなものを作成することです。T&

template <size_t DOF>
class factory
{
private:

    template <template <typename> class Target, typename T, typename... Index>
    static typename std::enable_if<(sizeof...(Index) < DOF), Target<T>>::type
    _do_construct(T* array, Index... index)
    {
        return _do_construct<Target>(array, index..., sizeof...(Index));
    }   

    template <template <typename> class Target, typename T, typename... Index>
    static typename std::enable_if<sizeof...(Index) == DOF, Target<T>>::type
    _do_construct(T* array, Index... index)
    {
        return { array[index]... };
    }

public:

    template <template <typename> class Target, typename T>
    static Target<T> wrap_array(T* array)
    {
        return _do_construct<Target>(array);
    }
};

double* fこれで、ライブラリ提供をunknowns<double> F安全なビアに変換できるようになりました

    auto F = factory<4>::wrap_array<unknowns>(f);

これはかなりいいです。

を省略できればさらに良いのですが<4>、これを行う方法がわかりませんでした。おそらく、構造体が保持するメンバーの数を決定するために SFINAE を使用できるはずですが、std::is_constructible必要なことを実行できないようです。

これがどのように可能になるかを誰かが提案できますか?

4

2 に答える 2

1

問題は、コンパイル時に template 内のメンバーの数を決定することですstruct Target<typename T>。ここで、はメンバーが 0 個以上で構成されることが知られているTargetPODテンプレートであり、すぐに必要なのはです。structTTdouble

Tスカラーでさえないことと一致するソリューションが必要です。

これは、小さな制限が 1 つありますが、次の事実を利用することで解決できstruct Target<T>ますsizeof(Target<T>)struct X含まれているビットフィールドがより多くのメンバーを持つことができる のは事実ですが、ビットフィールドsizeof(X){unsigned|signed} intであるため、ステートメントは for struct Target<T>, for any に当てはまりますT

小さな制限は、Tデフォルトで構築可能であることです。

sizeof(Target<T>)は のフィールド数のコンパイル時の上限であるため、再帰的な SFINAEソリューションTarget<T>を作成できます。コンパイル時の再帰は、型 Tの初期化子が多すぎると aを初期化しようとする式の満たされない型に基づく置換の失敗によって、その制限から引き下げられます。そのフィールドの数は、最終的に受け入れた初期化子リストの長さであることを知っています。値が T に変換可能な型である場合、これらの投機的な初期化が試行される値を気にする必要はありません。Target<T>Target<T>

(もちろん、0 の初期化子から単純に再帰することはできません。なぜなら、あまり長くないTarget<T> 初期化子リストを受け入れるからです。)

Iこのようなソリューションを実装するには、任意の長さのタイプの初期化子リストを生成するコンパイル時の手段が必要です。ここで、Iは に変換可能Tです。Iが整数型である可能性がある 場合 、コンパイル時に整数シーケンスを生成するための従来技術のさまざまな例が思い浮かびます (SO C++ の著名人: ShaubWakeleyKühllの例を含む)。

この低抵抗線の難点は、明らかに必要な制約であり、整数Tから構築可能です。それは非スカラーを除外するものではありませんが、非常に足を踏み外してフィールドを狭めます。IT

ただし、問題は明らかです。投機的なイニシャライザリストを構成するものは気にしないのでT、それらはすべて同じTである可能性があり、それのみをデフォルトで構築可能であると規定すれば、T同一のものを見つけるのに問題はありません。T次に、これらの初期化子リストを構築するために、実際には整数型から構築可能である必要はありませんITそのから構築可能な中間型から構築可能である必要があるだけSですI。 そして、コンストラクターであり、 を返す必要なプロパティを使用して、たとえば、 forなどのテンプレートからそのようなを簡単に作成できます。Sshim<U>U = Tshim<U>(I)shim<U> operator T() constU()

ここまでは順調ですね。しかし、より一般的な解決策は今カードにあるのでしょうか?

T 受け入れられるintializer-list-of- の最大長を見つける方法を検討しており、テンプレートの特性に関する前提条件を考慮Target<T>して、テンプレート内のフィールド数を推測できます。Targetこれらの前提条件を放棄したとします。それTargetはテンプレートTarget<T>です。そのすべてのフィールドがタイプであることT。それがPODであること。

次に、長さ <= 任意の制限のTarget初期化子リストで任意の型を構築できるかどうかを判断する compiletine メソッドを引き続き検討します。これは、予備的なアイデアよりも役立つ可能性があります (ただし、十分に再確認します)。TM

この余分な一般性の些細なコストは、テンプレート ソリューションのインターフェイスが、問題ごとにテンプレートがオンになっている ときTarget、単純にパラメーター化できなくなることです。その場合、 および でパラメータ化する必要があり ます。より重要なペナルティは、テンプレート インターフェイスをパラメータ化する必要があるという事実です。これは、イニシャライザ リストが検索される制限の長さです。なんで?ifが POD ではないため、 thenは受け入れられる初期化子の数の上限ではなくなりました。TTarget TTarget<T>TMTargetTargetsizeof(Target)Target

そのような必要性は、Mあなた自身のソリューションについてあなたが気に入らなかったことです。Target しかし、より一般的な解決策では、 PODの場合は常にその必要性を回避できます 。そのプロパティは によって検出可能で std::is_pod<Target>::value == trueあるため、より一般的な解決策はM 、その場合はデフォルトにすることができsizeof(Target)、それ以外の場合はまったくデフォルトにしないことができます。

次の解決策は、これらすべての残りです。私の compiletime-integer-sequences 装置について、C++ 標準委員会のメンバーである Daniel Krüglerを盗用することにしました。

make_indices.h

#ifndef MAKE_INDICES_H
#define MAKE_INDICES_H

/* After [Daniel Krügler]
    https://groups.google.com/forum/?fromgroups#!topic/comp.lang.c++.moderated/H6icuxL0NAY

*/  
template<unsigned...> struct indices {}; 

namespace detail {

template<unsigned I, class Indices, unsigned N> 
struct make_indices; 

template<unsigned I, unsigned... Indices, unsigned N> 
struct make_indices<I, indices<Indices...>, N> 
{ 
    typedef typename make_indices<I + 1, indices<Indices..., I>, N>::type type; 
}; 

template<unsigned N, unsigned... Indices> 
struct make_indices<N, indices<Indices...>, N> 
{ 
    typedef indices<Indices...> type; 
};

} // namespace detail 

template<unsigned N> 
struct make_indices : detail::make_indices<0, indices<>, N> {}; 

#endif // EOF

それから私の貢献:initializer_count.h

#ifndef INITIALIZER_COUNT_H
#define INITIALIZER_COUNT_H

#include "make_indices.h"
#include <type_traits>

namespace detail {

/* class detail::shim<U> is a convenience wrapper of U whose
    sole purpose is to be constructible from unsigned and
    convertible to a U.
*/  
template<typename U>
struct shim
{
    static_assert(std::is_default_constructible<U>::value,
        "U must be default-constructible for detail::shim<U>");
    explicit shim(unsigned){};
    operator U () const {
        return U();
    }
};

} // namespace detail

/*
    class initializer_count<Target,T> will export 
    `static const unsigned value` == the maximum length <= Size of
    initializer list of T that Target will accept. 

    Size defaults to sizeof(Target) if Target id POD. Otherwise a static_assert
    is tripped if Size is defaulted.
*/
template<
    class Target, 
    typename T, 
    unsigned Size = std::is_pod<Target>::value ? sizeof(Target) : unsigned(-1)
>
struct initializer_count;

// Terminal case
template<class Target, typename T>
struct initializer_count<Target,T,0>
{
    static const unsigned value = 0;
};

// Recursion case.
template<class Target, typename T, unsigned Size>
struct initializer_count
{
    static_assert(Size != unsigned(-1),
        "Size cannot be defaulted for non-POD "
        "Target in initializer_count<Target,T,Size>");

    // SFINAE success. Target can be initialized with a list of length Size 
    template<unsigned ...I>
    static constexpr auto count(indices<I...>) -> 
            decltype(Target{detail::shim<T>(I)...},Size) {
        return Size;
    }

    // SFINAE failure.
    template<unsigned ...I>
    static constexpr unsigned count(...) {
        // Recurse to Size - 1
        return initializer_count<Target,T,Size - 1>::value;
    }

    static const unsigned value = count(typename make_indices<Size>::type());
};

#endif // EOF

テスト プログラム (gcc 4.7.2/4.8.1、clang 3.2):

#include "initializer_count.h"

struct non_pod 
{
    non_pod(){}
    non_pod(double a, short b)
    : _a(a),_b(b){}
    double _a = 42.0;
    short _b = 42;
};

template <typename T>
struct five_unknowns
{
    T a;
    T b;
    T c;
    T d;
    T e;
};

template <typename T>
struct one_unknown
{
    T a;
};

template <typename T>
struct zero_unknowns {};

#include <iostream>

using namespace std;

int main()
{
    static const unsigned initializer_max = 100;
    static_assert(!std::is_pod<non_pod>::value,"");
    cout << initializer_count<zero_unknowns<char>,char>::value << endl;
    cout << initializer_count<one_unknown<int>,int>::value << endl;
    cout << initializer_count<five_unknowns<double>,double>::value << endl;
    // Need initializer_max for rest non-pod targets...
    cout << 
        initializer_count<five_unknowns<non_pod>,non_pod,initializer_max>::value
        << endl;
    cout << initializer_count<non_pod,short,initializer_max>::value << endl;
    return 0;
}

// EOF

期待される出力:

0
1
5
5
2
于 2013-06-22T19:08:25.587 に答える
0

別のラッパーはどうですか:

template <typename T, unsigned int N>
Target<T> wrap_actual_array(T const (&a)[N])
{
    return factory<N>::wrap_array<unkowns>(a);
}
于 2013-06-06T15:25:52.877 に答える