2014-10-31 10 views
7

Başlığın doğru olup olmadığından emin değilim ama sorun şu/problemim:C++ otomatik program oluşturma meta programlaması?

Belirli bir ifade için işlevler oluşturmak amacıyla meta programlamayı kullanmak istiyorum.

template<typename T1, typename T2> 
struct plus{ 
    T1 func(T1 in1, T2 in2){ return in1 + in2; } 
}; 

template<typename T1, typename T2, typename T3, typename expr> 
struct wrap{ 

    /* contain a func that can evaluate the expr */ 
}; 

ve programcı İfade için bir işlev yaratmak amacıyla feryat kod yazacak:

wrap<int,int,int,plus<plus<int,int>,int> >::func(1,2,3); /*result should be 6*/ 

bu mümkün mü Örneğin bu kodu var olduğunu söylemek sağlar?

Teşekkür ederiz.

+2

Olası: Evet. Önerilen: Şüpheli. Orada şablon metaprogramlamayı seven birçok insan varken, bundan kaçınmaya çalışan insanlar da var. Sorun şu ki, bir derleyici hatası size tükürmedikçe, şablonlarınızın genişletilmiş sürümünü hiç görmediniz. Özellikle de, sorunuzu çözen ifade şablonları, masum bir kod üzerinde, okunamayan hata mesajlarının korkunç bir karmaşasını yaratmasıyla ünlüdür. Tavsiyem: Eğer kod üretmek istiyorsanız, dürüst olun ve bir senaryo yazınız (python, perl, m4, ...). Böylece hem senaryonuzu hem de kodları okuyabilirsiniz. – cmaster

+0

basitçe 'auto func = [] (auto x, auto y, auto z) {return x + y + z;};' 'yazıp' func (1,2,3) 'aramasıyla ne yazıyor? – davidhigh

cevap

7
#include <utility> 
#include <tuple> 
#include <cstddef> 

struct arg 
{ 
    template <typename Arg1> 
    static constexpr decltype(auto) apply(Arg1&& arg1) 
    { 
     return std::forward<Arg1>(arg1); 
    } 

    static constexpr std::size_t arity = 1; 
}; 

template <typename Type, Type value> 
struct constant 
{  
    static constexpr decltype(auto) apply() 
    { 
     return value; 
    } 

    static constexpr std::size_t arity = 0; 
}; 

template <typename Lhs, typename Rhs> 
struct plus 
{ 
    template <typename... Args> 
    static constexpr decltype(auto) apply(Args&&... args) 
    { 
     return _apply(std::make_index_sequence<Lhs::arity>{}, std::make_index_sequence<Rhs::arity>{}, std::tuple<Args&&...>(std::forward<Args>(args)...)); 
    } 

    template <typename Tuple, std::size_t... Arity1, std::size_t... Arity2> 
    static constexpr decltype(auto) _apply(std::index_sequence<Arity1...>, std::index_sequence<Arity2...>, Tuple&& args) 
    { 
     return Lhs::apply(static_cast<typename std::tuple_element<Arity1, Tuple>::type>(std::get<Arity1>(args))...) 
      + Rhs::apply(static_cast<typename std::tuple_element<Lhs::arity + Arity2, Tuple>::type>(std::get<Lhs::arity + Arity2>(args))...); 
    } 

    static constexpr std::size_t arity = Lhs::arity + Rhs::arity; 
}; 

template <typename Lhs, typename Rhs> 
struct multiply 
{ 
    template <typename... Args> 
    static constexpr decltype(auto) apply(Args&&... args) 
    { 
     return _apply(std::make_index_sequence<Lhs::arity>{}, std::make_index_sequence<Rhs::arity>{}, std::tuple<Args&&...>(std::forward<Args>(args)...)); 
    } 

    template <typename Tuple, std::size_t... Arity1, std::size_t... Arity2> 
    static constexpr decltype(auto) _apply(std::index_sequence<Arity1...>, std::index_sequence<Arity2...>, Tuple&& args) 
    { 
     return Lhs::apply(static_cast<typename std::tuple_element<Arity1, Tuple>::type>(std::get<Arity1>(args))...) 
      * Rhs::apply(static_cast<typename std::tuple_element<Lhs::arity + Arity2, Tuple>::type>(std::get<Lhs::arity + Arity2>(args))...); 
    } 

    static constexpr std::size_t arity = Lhs::arity + Rhs::arity; 
}; 

Testi:

int main() 
{ 
    // (1 + 2) + 3 = 6 
    std::cout << plus<plus<arg, arg>, arg>::apply(1, 2, 3) << std::endl; 

    // (a + 5) + (2 * 6) = 9 + 12 = 21 
    int a = 4; 
    std::cout << plus<plus<arg, arg>, multiply<arg, constant<int, 6>>>::apply(a, 5, 2) << std::endl; 

    // ((1 * 2) * 3) * 4 = 24 
    std::cout << multiply<multiply<multiply<arg, arg>, arg>, arg>::apply(1, 2, 3, 4) << std::endl; 

    // 2 + (4 * 5) = 22 
    static_assert(plus<arg, multiply<arg, arg>>::apply(2, 4, 5) == 22, "!"); 
} 
Çıkış

: Yeni fanktorlar sokulması daha az çaba gerektirir, böylece

6 
21 
24 

DEMO 1


yukarıdaki çözüm geliştirilebilir ve decl arations kendileri aşağıda gibi daha okunabilir:

#include <iostream> 
#include <utility> 
#include <tuple> 
#include <cstddef> 

template <std::size_t Arity> 
struct expression 
{  
    static constexpr std::size_t arity = Arity; 
}; 

template <typename Expr, typename Rhs> 
struct unary_expression : expression<Rhs::arity> 
{  
    template <typename... Args> 
    static constexpr decltype(auto) apply(Args&&... args) 
    { 
     static_assert(sizeof...(Args) == unary_expression::arity, "Wrong number of operands!"); 
     return Expr::eval(Rhs::apply(std::forward<Args>(args)...)); 
    } 
}; 

template <typename Expr, typename Lhs, typename Rhs> 
struct binary_expression : expression<Lhs::arity + Rhs::arity> 
{ 
    template <typename... Args> 
    static constexpr decltype(auto) apply(Args&&... args) 
    { 
     static_assert(sizeof...(Args) == binary_expression::arity, "Wrong number of operands!"); 
     return _apply(std::make_index_sequence<Lhs::arity>{}, std::make_index_sequence<Rhs::arity>{}, std::tuple<Args&&...>(std::forward<Args>(args)...)); 
    } 

    template <typename Tuple, std::size_t... Arity1, std::size_t... Arity2> 
    static constexpr decltype(auto) _apply(std::index_sequence<Arity1...>, std::index_sequence<Arity2...>, Tuple&& args) 
    { 
     return Expr::eval(Lhs::apply(static_cast<typename std::tuple_element<Arity1, Tuple>::type>(std::get<Arity1>(args))...), 
          Rhs::apply(static_cast<typename std::tuple_element<Lhs::arity + Arity2, Tuple>::type>(std::get<Lhs::arity + Arity2>(args))...)); 
    } 
}; 

struct arg : expression<1> 
{ 
    template <typename Arg1> 
    static constexpr decltype(auto) apply(Arg1&& arg1) 
    { 
     return std::forward<Arg1>(arg1); 
    } 
}; 

template <typename Type, Type value> 
struct constant : expression<0> 
{  
    static constexpr decltype(auto) apply() 
    { 
     return value; 
    } 
}; 

template <typename Rhs> 
struct negate : unary_expression<negate<Rhs>, Rhs> 
{ 
    template <typename Arg1> 
    static constexpr decltype(auto) eval(Arg1&& arg1) 
    { 
     return -std::forward<Arg1>(arg1); 
    } 
}; 

template <typename Lhs, typename Rhs> 
struct plus : binary_expression<plus<Lhs, Rhs>, Lhs, Rhs> 
{ 
    template <typename Arg1, typename Arg2> 
    static constexpr decltype(auto) eval(Arg1&& arg1, Arg2&& arg2) 
    { 
     return std::forward<Arg1>(arg1) + std::forward<Arg2>(arg2); 
    } 
}; 

template <typename Lhs, typename Rhs> 
struct minus : binary_expression<minus<Lhs, Rhs>, Lhs, Rhs> 
{ 
    template <typename Arg1, typename Arg2> 
    static constexpr decltype(auto) eval(Arg1&& arg1, Arg2&& arg2) 
    { 
     return std::forward<Arg1>(arg1) - std::forward<Arg2>(arg2); 
    } 
}; 

template <typename Lhs, typename Rhs> 
struct multiply : binary_expression<multiply<Lhs, Rhs>, Lhs, Rhs> 
{ 
    template <typename Arg1, typename Arg2> 
    static constexpr decltype(auto) eval(Arg1&& arg1, Arg2&& arg2) 
    { 
     return std::forward<Arg1>(arg1) * std::forward<Arg2>(arg2); 
    } 
}; 

int main() 
{  
    // (1 + 2) + 3 = 6 
    std::cout << plus<plus<arg, arg>, arg>::apply(1, 2, 3) << std::endl; 

    // ((a + 5) + (2 * 6)) - 5 = 16 
    int a = 4; 
    std::cout << minus<plus<plus<arg, arg>, multiply<arg, constant<int, 6>>>, constant<int, 5>>::apply(a, 5, 2) << std::endl; 

    // ((1 * 2) * 3) * 4 = 24 
    std::cout << multiply<multiply<multiply<arg, arg>, arg>, arg>::apply(1, 2, 3, 4) << std::endl; 

    // -((3 * 4) + (5 - 6)) = -11 
    static_assert(negate<plus<multiply<arg, arg>, minus<arg, arg>>>::apply(3, 4, 5, 6) == -11, "!"); 
} 

DEMO 2

+0

+1. Daha fazla bir soyutlama olarak, 'artı' ve 'eksi' öğelerinin kodlamasını engelleyebilir ve herhangi bir ikili işleve izin verebilir ("constexpr"). Bu, çoğunlukla özdeş koddan kaçınabildiğiniz (yalnızca + ve '* ile farklılık gösterdiklerinden). – davidhigh

+0

@davidhigh bu [DEMO 2] 'de yaptığım iştir (http://coliru.stacked-crooked.com/a/c4ee9c7ecbf29a75), burada ortak kod "binary_expression" sınıfı –

+0

@ Eldrad'a taşındı: Kesinlikle kullanmak istiyorsunuz Bu int 'diğer türler için, sanırım? Çünkü biliyorsun, ör., 'wrap , çarpın >>> :: func (a, 5, 2)', (a + 5) + (2 * 6) 'ile eşdeğerdir, ikincisi açıkça yazmak için çok daha kolay ... – davidhigh

2

Kesinlikle. Bunlar "ifade şablonları" olarak adlandırılır ve SO vurgularını here bulabilirsiniz.

POOMA sisteminde 90'ların sonunda paralel programlama için çalıştım. Modern standartlara güncellenmiş olup olmadığından emin değilim, ancak bunun hala çevrimiçi olarak here olduğunu görüyorum. Altta yatan POOMA, diğer değerlendirme motorları için yeniden tasarlanabilen PETE adı verilen bir "İfade Şablon Motoru" idi. PETE, here tarif edilmektedir. Tüm bu çalışma C++ 11 ile çok daha kolay olurdu ve eminim ki bu yeni yetenekleri kullanan benzer çabalar var.

+2

OP'nin yayınladığı bilgilere dayanarak kısa bir örnek kod verebilir misiniz, lütfen? –

+0

Şimdi biraz çalışıyorum - şu anda çalışıyor. :) – sfjac