2014-10-31 17 views
7

Tôi không chắc chắn nếu tiêu đề là chính xác nhưng đây là vấn đề/câu hỏi của tôi:C + + lập trình meta tự động tạo chức năng?

Tôi muốn sử dụng lập trình meta để tạo các hàm cho một biểu thức cụ thể. Ví dụ cho phép nói rằng chúng tôi có mã này:

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 */ 
}; 

và các lập trình viên sẽ viết mã dưới đây để tạo một hàm cho một biểu thức:

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

Đây có phải là có thể?

Cảm ơn bạn.

+2

có thể: Có. Có thể tư vấn: Tranh chấp. Trong khi có rất nhiều người ở đó, những người yêu thích lập trình meta mẫu, cũng có những người cố gắng tránh nó. Vấn đề là, bạn không bao giờ nhìn thấy phiên bản mở rộng của các mẫu của bạn trừ khi một lỗi trình biên dịch phun nó ra ở bạn. Đặc biệt là các mẫu biểu hiện giải quyết câu hỏi của bạn là khét tiếng để tạo ra một mớ hỗn độn khủng khiếp của các thông báo lỗi không thể đọc được trên mã dường như vô tội. Lời khuyên của tôi: Nếu bạn muốn tạo mã, hãy thành thật về nó và viết một kịch bản (python, perl, m4, ...). Bằng cách đó bạn có thể đọc cả, kịch bản và mã được tạo. – cmaster

+0

đơn giản là viết 'auto func = [] (auto x, auto y, auto z) {return x + y + z;}; ', theo sau là một cuộc gọi' func (1,2,3) '? – davidhigh

Trả lời

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; 
}; 

Test:

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, "!"); 
} 

Output:

6 
21 
24 

DEMO 1


Các giải pháp trên có thể được cải tiến để giới thiệu functors mới đòi hỏi phải có nỗ lực ít hơn, và từ chối arations chính họ là dễ đọc hơn, như dưới đây:

#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. Như một trừu tượng hơn nữa, bạn có thể tránh mã hóa cứng các công cụ 'plus' và' trừ' và cho phép bất kỳ hàm nhị phân nào (tức là 'constexpr'). Điều này bạn có thể tránh mã chủ yếu giống hệt nhau (vì chúng chỉ khác nhau bởi '+' và '*'). – davidhigh

+0

@ davidhigh đây là những gì tôi đã làm trong [DEMO 2] (http://coliru.stacked-crooked.com/a/c4ee9c7ecbf29a75), trong đó mã phổ biến được chuyển đến lớp 'binary_expression' –

+0

@Eldrad: bạn chắc chắn muốn sử dụng điều này cho các loại khác với 'int', tôi đoán vậy? Vì bạn biết điều đó, ví dụ:, 'wrap , nhân >>> :: func (a, 5, 2)' tương đương với '(a + 5) + (2 * 6)', trong khi thứ hai rõ ràng là dễ dàng hơn để viết ... – davidhigh

2

Tuyệt đối. Chúng được gọi là "mẫu biểu thức" và bạn có thể tìm thấy các điểm nổi bật SO here.

Tôi đã làm việc trên hệ thống POOMA để lập trình song song vào cuối những năm 90. Không chắc chắn nếu nó được cập nhật theo tiêu chuẩn hiện đại, nhưng tôi thấy rằng nó vẫn có sẵn trực tuyến here. POOMA bên dưới là một "Máy tạo mẫu biểu thức" được gọi là PETE có thể được đặt lại cho các công cụ đánh giá khác. PETE được mô tả here. Tất cả công việc đó sẽ đơn giản hơn nhiều với C++ 11 và tôi chắc rằng có những nỗ lực tương tự ở đó sử dụng những khả năng mới hơn này.

+2

Bạn có thể đưa ra một mã mẫu ngắn gọn, dựa trên những gì OP đã đăng không? –

+0

Tôi sẽ xem xét sau khi làm việc một chút ngay bây giờ. :) – sfjac

Các vấn đề liên quan