SORU
10 Ocak 2013, PERŞEMBE


Nasıl variadic bir şablon işlevi heterojen bir argüman paketleri üzerinde genel hesaplamaları yapmak için?

ÖNCÜL:

Etrafında variadic şablonları biraz uğraştıktan sonra, biraz meta-programlama görevler önemsiz yakında ötesinde elde bir şey oldukça hantal hale geldiğini fark ettim. Özellikle, bir şekilde gerçekleştirmek isteyen kendimi buldumbir tartışma üzerine Genel işlemler packgibiyineleme,böl,döngüstd::for_eachgibi moda.

Sonra izlerken this lecture by Andrei Alexandrescu C ve Ötesi 2012 cazibe static if C (inşa ödünç gelen D Programming Language) hissettiğim bir çeşit static for gelirdi kullanışlı olarak iyi hissediyorum daha bu static yapıları getirebilir yarar.

Eğer elde etmek için bir yol olup olmadığını merak etmeye başladımbunun gibi bir şeyvariadic bir şablon bağımsız işlev paketleri (pseudo-code):

template<typename... Ts>
void my_function(Ts&&... args)
{
    static for (int i = 0; i < sizeof...(args); i  ) // PSEUDO-CODE!
    {
        foo(nth_value_of<i>(args));
    }
}

Çevrilmiş olacaktı-derlemeböyle bir şeye:

template<typename... Ts>
void my_function(Ts&&... args)
{
    foo(nth_value_of<0>(args));
    foo(nth_value_of<1>(args));
    // ...
    foo(nth_value_of<sizeof...(args) - 1>(args));
}

Prensip olarak, static_for daha ayrıntılı işlemek için izin verir:

template<typename... Ts>
void foo(Ts&&... args)
{
    constexpr s = sizeof...(args);

    static for (int i = 0; i < s / 2; i  )
    {
        // Do something
        foo(nth_value_of<i>(args));
    }

    static for (int i = s / 2; i < s; i  )
    {
        // Do something different
        bar(nth_value_of<i>(args));
    }
}

Ya da daha anlamlı bir deyim için böyle

template<typename... Ts>
void foo(Ts&&... args)
{
    static for_each (auto&& x : args)
    {
        foo(x);
    }
}

İLGİLİ ÇALIŞIR:

Web üzerinde biraz araştırma yaptım ve öğrendimbir şeygerçekten de var:

  • This link artış parametre bir paket dönüştürmek açıklamaktadır.Vektör MPL, bir şekilde (az) hedefe doğru gitmektedir
  • this question on SO gibi aramak için bir benzer ve biraz ilgili meta-programlama özelliği (yarma bir argüman paketi içine iki yarısı) - aslında, birkaç soru bulunmaktadır ÇOK hangi galiba bu konuyla ilgili, ama hiçbir cevap var oku çözer bu tatmin edici IMHO;
  • Boost.Fusion bir bağımsız değişken bir bohça içine dönüştürme algoritmaları tanımlardemetama ben tercih ederim:
    1. oluşturmak için değilgereksiz geçiciolabilir (ve olmalıdır) mükemmel argümanlar tutmak için bazı genel algoritmalar iletilmek üzere;
    2. birküçük, kendi kendine yetenkütüphane Boost süre için.Füzyon bu sorunu çözmek için gereken daha fazla şeyler vardır muhtemelen.

SORU:

Mevcut yaklaşımların sınırlamaları üstlenmeden olmadan aradığım şeyi elde etmek için nispeten basit bir şekilde, belki bazı şablon yoluyla meta-programlama, var mı?

CEVAP
10 Ocak 2013, PERŞEMBE


Buldum ne mutlu olduğumdan beri, bir çözüm kendim çözmeye çalıştım ve bir yazılı sona erdiküçük bir kütüphanetartışma paketleri formüle genel işlemler sağlar. Benim çözüm aşağıdaki özelliklere sahiptir:

  • Yineleme yerinde sağlarbazıbağımsız değişken unsurları, muhtemelen belirtilen paketleyinbilgisayarpaketi kendi endeksleri;
  • Yönlendirme sağlar bir tartışma bilgisayarlı bölümleri funktorlar variadic paketi;
  • Nispeten kısa bir başlığı da dahil olmak üzere gerektirir; dosya . sadece
  • Mükemmel iletme yaygın kullanımı ağır satır içi uygulaması için izin vermek için yapar ve gereksiz kopya/önler en az performans kaybı için izin vermek için hareket eder;
  • Yineleme algoritmaları iç uygulama hafıza kullanımını en aza indirmek için Boş bir Temel Sınıf Optimizasyonu dayanır;
  • Kolay (nispeten, dikkate şablon meta-programlama) genişletmek ve uyum.

İlk göstereceğimne yapılabilirkütüphanesine sahipsonrapost onunuygulama.

DURUMLARDA KULLANIN

İşte bir örnek nasıl for_each_in_arg_pack() fonksiyon için kullanılabilir yineleme aracılığıyla tüm argümanlar bir sürü geçmek ve her bir bağımsız giriş için bazı istemci tarafından sağlanan functor (elbette, eşleme olmalı genel bir çağrı operatörü argüman paketi içeren değerler heterojen tipleri):

// Simple functor with a generic call operator that prints its input. This is used by the
// following functors and by some demonstrative test cases in the main() routine.
struct print
{
    template<typename T>
    void operator () (T&& t)
    {
        cout << t << endl;
    }
};

// This shows how a for_each_*** helper can be used inside a variadic template function
template<typename... Ts>
void print_all(Ts&&... args)
{
    for_each_in_arg_pack(print(), forward<Ts>(args)...);
}

print eşleme yukarıda da daha karmaşık hesaplamalar için kullanılabilir. Özellikle, burada tek bir yinelemealt(bu durumda, biralt aralığıbir paketi argümanlar:

// Shows how to select portions of an argument pack and 
// invoke a functor for each of the selected elements
template<typename... Ts>
void split_and_print(Ts&&... args)
{
    constexpr size_t packSize = sizeof...(args);
    constexpr size_t halfSize = packSize / 2;

    cout << "Printing first half:" << endl;
    for_each_in_arg_pack_subset(
        print(), // The functor to invoke for each element
        index_range<0, halfSize>(), // The indices to select
        forward<Ts>(args)... // The argument pack
        );

    cout << "Printing second half:" << endl;
    for_each_in_arg_pack_subset(
        print(), // The functor to invoke for each element
        index_range<halfSize, packSize>(), // The indices to select
        forward<Ts>(args)... // The argument pack
        );
}

Bazen sadece yapmak isteyebilirileri bir bölümünübir değişkeni başka variadic pack öğeleri geçmek ve kendi arasında dolaşmak yerine her functorayrı ayrıolmayan variadic bir functor. Bu forward_subpack() algoritma yapmak mümkün oluyor:

// Functor with variadic call operator that shows the usage of for_each_*** 
// to print all the arguments of a heterogeneous pack
struct my_func
{
    template<typename... Ts>
    void operator ()(Ts&&... args)
    {
        print_all(forward<Ts>(args)...);
    }
};

// Shows how to forward only a portion of an argument pack 
// to another variadic functor
template<typename... Ts>
void split_and_print(Ts&&... args)
{
    constexpr size_t packSize = sizeof...(args);
    constexpr size_t halfSize = packSize / 2;

    cout << "Printing first half:" << endl;
    forward_subpack(my_func(), index_range<0, halfSize>(), forward<Ts>(args)...);

    cout << "Printing second half:" << endl;
    forward_subpack(my_func(), index_range<halfSize, packSize>(), forward<Ts>(args)...);
}

Daha özel görevler için, bir paketi belirli değişkenleri ile almak için tabi ki olabilirdizin oluşturmaonlara. Bu nth_value_of() işlevi, kendi yardımcıları ile birlikte first_value_of() last_value_of() yapmayı sağlayan budur:

// Shows that arguments in a pack can be indexed
template<unsigned I, typename... Ts>
void print_first_last_and_indexed(Ts&&... args)
{
    cout << "First argument: " << first_value_of(forward<Ts>(args)...) << endl;
    cout << "Last argument: " << last_value_of(forward<Ts>(args)...) << endl;
    cout << "Argument #" << I << ": " << nth_value_of<I>(forward<Ts>(args)...) << endl;
}

Bağımsız paketihomojendiğer taraftan (bağımsız değişken aynı tip var yani, aşağıdaki gibi bir formülasyon tercih olabilir. is_homogeneous_pack<> meta-fonksiyonu parametre paketi tüm türleri homojen olup olmadığını belirlemek izin verir, ve özellikle static_assert() açıklamaları: içinde kullanılmak üzere tasarlanmıştır

// Shows the use of range-based for loops to iterate over a
// homogeneous argument pack
template<typename... Ts>
void print_all(Ts&&... args)
{
    static_assert(
        is_homogeneous_pack<Ts...>::value, 
        "Template parameter pack not homogeneous!"
        );

    for (auto&& x : { args... })
    {
        // Do something with x...
    }

    cout << endl;
}

Son olarak, bu yanaLambdasadecesözdizimsel şekerfunktorlar, algoritmalar ile birlikte de yukarıda; ancak bu kadar kullanılabilirgenel LambdaC tarafından desteklenecek , bu mümkünhomojentartışma paketleri. Aşağıdaki örnek, aynı zamanda homojen bir paket: tüm bağımsız değişkenlerin türünü döndürür homogeneous-type<> meta-işlev, kullanımını gösterir

 // ...
 static_assert(
     is_homogeneous_pack<Ts...>::value, 
     "Template parameter pack not homogeneous!"
     );
 using type = homogeneous_type<Ts...>::type;
 for_each_in_arg_pack([] (type const& x) { cout << x << endl; }, forward<Ts>(args)...);

Bu kütüphane yapmayı sağlar ... aslında, ama ben inanıyorumgenişletilmiş olabilir biledaha karmaşık görevleri yerine getirmek.

UYGULAMA

Şimdi gelir kodu açıklamak ve bu yazı çok uzun (belki de) yapmaktan kaçınmak için yorum güveniyor ben biraz kendi içinde zor olan bu uygulama,:

#include <type_traits>
#include <utility>

//===============================================================================
// META-FUNCTIONS FOR EXTRACTING THE n-th TYPE OF A PARAMETER PACK

// Declare primary template
template<int I, typename... Ts>
struct nth_type_of
{
};

// Base step
template<typename T, typename... Ts>
struct nth_type_of<0, T, Ts...>
{
    using type = T;
};

// Induction step
template<int I, typename T, typename... Ts>
struct nth_type_of<I, T, Ts...>
{
    using type = typename nth_type_of<I - 1, Ts...>::type;
};

// Helper meta-function for retrieving the first type in a parameter pack
template<typename... Ts>
struct first_type_of
{
    using type = typename nth_type_of<0, Ts...>::type;
};

// Helper meta-function for retrieving the last type in a parameter pack
template<typename... Ts>
struct last_type_of
{
    using type = typename nth_type_of<sizeof...(Ts) - 1, Ts...>::type;
};

//===============================================================================
// FUNCTIONS FOR EXTRACTING THE n-th VALUE OF AN ARGUMENT PACK

// Base step
template<int I, typename T, typename... Ts>
auto nth_value_of(T&& t, Ts&&... args) ->
    typename std::enable_if<(I == 0), decltype(std::forward<T>(t))>::type
{
    return std::forward<T>(t);
}

// Induction step
template<int I, typename T, typename... Ts>
auto nth_value_of(T&& t, Ts&&... args) ->
    typename std::enable_if<(I > 0), decltype(
        std::forward<typename nth_type_of<I, T, Ts...>::type>(
            std::declval<typename nth_type_of<I, T, Ts...>::type>()
            )
        )>::type
{
    using return_type = typename nth_type_of<I, T, Ts...>::type;
    return std::forward<return_type>(nth_value_of<I - 1>((std::forward<Ts>(args))...));
}

// Helper function for retrieving the first value of an argument pack
template<typename... Ts>
auto first_value_of(Ts&&... args) ->
    decltype(
        std::forward<typename first_type_of<Ts...>::type>(
            std::declval<typename first_type_of<Ts...>::type>()
            )
        )
{
    using return_type = typename first_type_of<Ts...>::type;
    return std::forward<return_type>(nth_value_of<0>((std::forward<Ts>(args))...));
}

// Helper function for retrieving the last value of an argument pack
template<typename... Ts>
auto last_value_of(Ts&&... args) ->
    decltype(
        std::forward<typename last_type_of<Ts...>::type>(
            std::declval<typename last_type_of<Ts...>::type>()
            )
        )
{
    using return_type = typename last_type_of<Ts...>::type;
    return std::forward<return_type>(nth_value_of<sizeof...(Ts) - 1>((std::forward<Ts>(args))...));
}

//===============================================================================
// METAFUNCTION FOR COMPUTING THE UNDERLYING TYPE OF HOMOGENEOUS PARAMETER PACKS

// Used as the underlying type of non-homogeneous parameter packs
struct null_type
{
};

// Declare primary template
template<typename... Ts>
struct homogeneous_type;

// Base step
template<typename T>
struct homogeneous_type<T>
{
    using type = T;
    static const bool isHomogeneous = true;
};

// Induction step
template<typename T, typename... Ts>
struct homogeneous_type<T, Ts...>
{
    // The underlying type of the tail of the parameter pack
    using type_of_remaining_parameters = typename homogeneous_type<Ts...>::type;

    // True if each parameter in the pack has the same type
    static const bool isHomogeneous = std::is_same<T, type_of_remaining_parameters>::value;

    // If isHomogeneous is "false", the underlying type is the fictitious null_type
    using type = typename std::conditional<isHomogeneous, T, null_type>::type;
};

// Meta-function to determine if a parameter pack is homogeneous
template<typename... Ts>
struct is_homogeneous_pack
{
    static const bool value = homogeneous_type<Ts...>::isHomogeneous;
};

//===============================================================================
// META-FUNCTIONS FOR CREATING INDEX LISTS

// The structure that encapsulates index lists
template <unsigned... Is>
struct index_list
{
};

// Collects internal details for generating index ranges [MIN, MAX)
namespace detail
{
    // Declare primary template for index range builder
    template <unsigned MIN, unsigned N, unsigned... Is>
    struct range_builder;

    // Base step
    template <unsigned MIN, unsigned... Is>
    struct range_builder<MIN, MIN, Is...>
    {
        typedef index_list<Is...> type;
    };

    // Induction step
    template <unsigned MIN, unsigned N, unsigned... Is>
    struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
    {
    };
}

// Meta-function that returns a [MIN, MAX) index range
template<unsigned MIN, unsigned MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;

//===============================================================================
// CLASSES AND FUNCTIONS FOR REALIZING LOOPS ON ARGUMENT PACKS

// Implementation inspired by @jogojapan's answer to this question:
// http://stackoverflow.com/questions/14089637/return-several-arguments-for-another-function-by-a-single-function

// Collects internal details for implementing functor invocation
namespace detail
{
    // Functor invocation is realized through variadic inheritance.
    // The constructor of each base class invokes an input functor.
    // An functor invoker for an argument pack has one base class
    // for each argument in the pack

    // Realizes the invocation of the functor for one parameter
    template<unsigned I, typename T>
    struct invoker_base
    {
        template<typename F, typename U>
        invoker_base(F&& f, U&& u) { f(u); }
    };

    // Necessary because a class cannot inherit the same class twice
    template<unsigned I, typename T>
    struct indexed_type
    {
        static const unsigned int index = I;
        using type = T;
    };

    // The functor invoker: inherits from a list of base classes.
    // The constructor of each of these classes invokes the input
    // functor with one of the arguments in the pack.
    template<typename... Ts>
    struct invoker : public invoker_base<Ts::index, typename Ts::type>...
    {
        template<typename F, typename... Us>
        invoker(F&& f, Us&&... args)
            :
            invoker_base<Ts::index, typename Ts::type>(std::forward<F>(f), std::forward<Us>(args))...
        {
        }
    };
}

// The functor provided in the first argument is invoked for each
// argument in the pack whose index is contained in the index list
// specified in the second argument
template<typename F, unsigned... Is, typename... Ts>
void for_each_in_arg_pack_subset(F&& f, index_list<Is...> const& i, Ts&&... args)
{
    // Constructors of invoker's sub-objects will invoke the functor.
    // Note that argument types must be paired with numbers because the
    // implementation is based on inheritance, and one class cannot
    // inherit the same base class twice.
    detail::invoker<detail::indexed_type<Is, typename nth_type_of<Is, Ts...>::type>...> invoker(
        f,
        (nth_value_of<Is>(std::forward<Ts>(args)...))...
        );
}

// The functor provided in the first argument is invoked for each
// argument in the pack
template<typename F, typename... Ts>
void for_each_in_arg_pack(F&& f, Ts&&... args)
{
    for_each_in_arg_pack_subset(f, index_range<0, sizeof...(Ts)>(), std::forward<Ts>(args)...);
}

// The functor provided in the first argument is given in input the
// arguments in whose index is contained in the index list specified
// as the second argument.
template<typename F, unsigned... Is, typename... Ts>
void forward_subpack(F&& f, index_list<Is...> const& i, Ts&&... args)
{
    f((nth_value_of<Is>(std::forward<Ts>(args)...))...);
}

// The functor provided in the first argument is given in input all the
// arguments in the pack.
template<typename F, typename... Ts>
void forward_pack(F&& f, Ts&&... args)
{
    f(std::forward<Ts>(args)...);
}

SONUÇ

Benim bu soruya cevap (ve aslında . ben de sağlamış olsa da tabii ki, ^em>çünküaslında bu), alternatif ya da daha iyi çözümler özledim ki var - ayrı olanlar "" bölümünde bir soru. İlgili İşler olarak belirtilen eğer merak ediyorum

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • boniver

    boniver

    17 NİSAN 2006
  • Krumme1996

    Krumme1996

    21 EYLÜL 2009
  • My Name Is Jeff

    My Name Is J

    26 ŞUBAT 2008