2016-10-09 25 views
7

Tôi thường có một số hành vi nguyên mẫu tạo ra đầu ra dựa trên một số phương pháp thiết kế. Tôi mẫu phương pháp thiết kế, cho phép nhiều chức năng tôi cần. Tuy nhiên, đôi khi các phương pháp thiết kế được đưa ra trong thời gian chạy, vì vậy tôi thường được yêu cầu viết một tuyên bố chuyển đổi rất lớn. Nó thường trông như thế này:Thực hiện chức năng số nguyên templated dựa trên tham số thời gian chạy

enum class Operation 
{ 
    A, B 
}; 


template<Operation O> 
    void execute(); 

template<> 
    void execute<A>() 
    { 
     // ... 
    } 

template<> 
    void execute<B>() 
    { 
     // ... 
    } 

void execute(Operation o) 
{ 
    switch (o) 
    { 
    case Operation::A: return execute<Operation::A>(); 
    case Operation::B: return execute<Operation::B>(); 
    } 
} 

tôi tò mò về việc liệu có ai đã tìm ra một mô hình tốt đẹp cho hệ thống này - những hạn chế chính của phương pháp này là người ta phải gõ ra tất cả các kiểu liệt kê được hỗ trợ và làm bảo trì một số nơi nếu liệt kê mới được thực hiện.

e: Tôi nên thêm rằng các lý do gây rối với các mẫu biên dịch thời gian là cho phép trình biên dịch các phương thức nội tuyến trong HPC cũng như kế thừa các thuộc tính constexpr.

e2: có hiệu lực, tôi đoán những gì tôi yêu cầu là để trình biên dịch tạo tất cả các đường dẫn mã có thể bằng cách sử dụng cấu trúc chuyển đổi ngầm. Có lẽ một số phép thuật đệ quy mẫu?

+0

Điều gì về việc sử dụng thừa kế và đa hình? –

+0

Như tôi vừa bổ sung, điều quan trọng đặc biệt là trình biên dịch có thể thực hiện việc inlining và tối ưu hóa tại thời gian biên dịch (tức là toàn bộ cấu trúc mã có thể nhìn thấy và xác định). Nếu không có, các chức năng ảo rõ ràng sẽ giải quyết được vấn đề. – Shaggi

Trả lời

4

Nếu bạn thực sự muốn sử dụng các mẫu cho tác vụ này, bạn có thể sử dụng kỹ thuật tương tự như this one.

// Here second template argument default to the first enum value 
template<Operation o, Operation currentOp = Operation::A> 
// We use SFINAE here. If o is not equal to currentOp compiler will ignore this function. 
auto execute() -> std::enable_if<o == currentOp, void>::type 
{ 
    execute<currentOp>(); 
} 

// Again, SFINAE technique. Compiler will stop search if the template above has been instantiated and will ignore this one. But in other case this template will be used and it will try to call next handler. 
template<Operation o, Operation currentOp = Operation::A> 
void execute() 
{ 
    return execute<o, static_cast<Operation>(static_cast<int>(currentOp) + 1)(c); 
} 
+0

Thú vị (liên kết bị hỏng btw), tôi sẽ kiểm tra nó ra – Shaggi

+0

Tôi đã sửa liên kết. –

2
template<class F, std::size_t...Is> 
void magic_switch(std::size_t N, F&& f, std::index_sequence<Is...>){ 
    auto* pf = std::addressof(f); 
    using pF=decltype(pf); 
    using table_ptr = void(*)(pF); 
    static const table_ptr table[]={ 
    [](pF){ std::forward<F>(*pf)(std::integral_constant<std::size_t, Is>{}); }... 
    }; 
    return table[N](pf); 
} 
template<std::size_t Count, class F> 
void magic_switch(std::size_t N, F&& f){ 
    return magic_switch(N, std::forward<F>(f), std::make_index_sequence<Count>{}); 
} 

Điều này làm cho một bảng nhảy mà gọi một lambda trên một thời gian biên dịch liên tục, hái mà nhập dựa trên một hằng số thời gian chạy. Điều này rất giống với cách một câu lệnh case switch được biên dịch cho đôi khi.

void execute(Operation o) { 
    magic_switch<2>(std::size_t(o), [](auto I){ 
    execute<Operation(I)>(); 
    }); 
} 

Sửa đổi để trả về không trống là có thể, nhưng tất cả các chi nhánh phải trả về cùng một loại.

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