2016-06-19 18 views
8

Làm cách nào để tôi có thể đơn giản hóa Mã này?Làm cách nào để đơn giản hóa biến "tham số mẫu này" trong C++?

mfer::i_value* make_empty_value(mfer::tag tag_) 
{ 
    if (tag_ == mfer::tag::mwf_ble) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_ble>()); 
    } else if (tag_ == mfer::tag::mwf_chn) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_chn>()); 
    } else if (tag_ == mfer::tag::mwf_blk) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_blk>()); 
    } else if (tag_ == mfer::tag::mwf_seq) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_seq>()); 
    } else if (tag_ == mfer::tag::mwf_man) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_man>()); 
    } else if (tag_ == mfer::tag::mwf_ivl) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_ivl>()); 
    } else if (tag_ == mfer::tag::mwf_sen) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_sen>()); 
    } else if (tag_ == mfer::tag::mwf_wfm) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_wfm>()); 
    } else if (tag_ == mfer::tag::mwf_pre) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pre>()); 
    } else if (tag_ == mfer::tag::mwf_off) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_off>()); 
    } else if (tag_ == mfer::tag::mwf_nul) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_nul>()); 
    } else if (tag_ == mfer::tag::mwf_pnt) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pnt>()); 
    } else if (tag_ == mfer::tag::mwf_nte) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_nte>()); 
    } else if (tag_ == mfer::tag::mwf_txc) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_txc>()); 
    } else if (tag_ == mfer::tag::mwf_flt) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_flt>()); 
    } else if (tag_ == mfer::tag::mwf_skw) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_skw>()); 
    } else if (tag_ == mfer::tag::mwf_mss) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_mss>()); 
    } else if (tag_ == mfer::tag::mwf_pnm) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pnm>()); 
    } else if (tag_ == mfer::tag::mwf_pid) { 
     return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pid>()); 
    } 

    return nullptr; 
} 

ngắn gọn nêu,

  • mfer :: tag là liệt kê, được định nghĩa như enum tag {}; trong namespace mfer.

  • mfer :: i_value là lớp trừu tượng.

    class i_value {}; 
    
  • mfer :: t_value là lớp templated như,

    template <mfer::tag tag_type> 
    class t_value : public i_value {}; 
    

Tại thời điểm này, tôi không biết làm thế nào để đơn giản hóa this make_empty_value().

Lý tưởng nhất, tôi muốn làm cho nó như thế này:

mfer::i_value* make_empty_value(mfer::tag tag_) 
{ 
    return memory_manager::instance().add(new mfer::t_value<tag_>()); 
} 

Nhưng tôi biết rằng nó là mẫu, vì vậy ở trên người ta không có ý nghĩa.

Có ý tưởng nào đơn giản hóa mã này không? (Một số tính năng C++ hiện đại, thư viện Boost, v.v.)

+2

Xin đừng gửi mã của bạn như một hình ảnh. Đăng nó dưới dạng văn bản trong câu trả lời. – milleniumbug

+0

Bạn có thể thay đổi 'if' thành' switch/case' nếu 'mfer :: tag :: mwf_ *' là các hằng số. Điều này có thể tăng cường khả năng đọc. – ForceBru

+1

Bạn có thể chia sẻ định nghĩa của lớp 't_value' không? Ít nhất là các phần liên quan trong đó 'tag_type' ảnh hưởng đến định nghĩa của lớp này. Nó hoàn toàn có thể, dựa trên những gì bạn đang làm, rằng 't_value' không phải là một lớp mẫu nào cả. tag_type có thể được chuyển vào hàm tạo. – selbie

Trả lời

2

Với một chút mẫu làm việc, chúng tôi có thể nhận được các chức năng nhà máy xuống:

i_value* make_empty_value(tag tag_type) 
{ 
    static constexpr auto factory = make_factory(all_tags()); 

    auto index = std::size_t(tag_type - tag::first); 
    if (index < tag::ntags) { 
     return memory_manager::instance().add(factory[index]()); 
    } 
    else { 
     return nullptr; 
    } 
} 

Full mã dưới đây.

Bản đồ trình tạo i_value được xây dựng tại thời gian biên dịch, cho phép tra cứu liên tục.

chế:

  • các giá trị trong enum phải liên tiếp, nhưng họ không cần phải bắt đầu từ số không.

  • bản trình diễn này yêu cầu C++ 14. Nó có thể dễ dàng thích nghi để làm việc với C++ 11. Đối với C++ 03, chúng tôi muốn tiếp cận để tăng mpl hoặc boost_pp.

hoàn thành ví dụ làm việc:

#include <array> 
#include <utility> 
#include <deque> 
#include <iostream> 

// minimal implementation of virtual base 
class i_value { 
public: 
    virtual void prove() const = 0; 
    virtual ~i_value() = default; 
}; 

// tag enum - note that we have supplied some extra introspection information 
// these could just as well be constexpr integers outside the enum 
enum tag 
{ 
    ble, 
    chn, 
    blk, 
    seq, 

    first = ble,    // first available tag 
    last = seq,    // last available tag 
    ntags = last-first  // number of tags 
}; 

/// Function to offset an index sequence by the distance from 
/// zero to the first available tag - in case the first tag is not zero 
template<std::size_t...tags> 
constexpr auto tag_offset(std::index_sequence<tags...>) 
{ 
    return std::index_sequence<(tags + tag::first)...>(); 
} 

/// Function to compute an index sequence of all valid tags 
constexpr auto all_tags() 
{ 
    return tag_offset(std::make_index_sequence<std::size_t(ntags)>()); 
} 

/// Factory function to generate a derived class for a given tag 
template <tag tag_type> 
class t_value : public i_value { 
    void prove() const override { void(std::cout << "I have tag " << tag_type << std::endl); } 
    ~t_value() { void(std::cout << "tag " << tag_type << " destroyed" << std::endl); } 
}; 

template<tag tag_type> 
i_value* make_instance() 
{ 
    return new t_value<tag_type>(); 
} 


/// Function to generate a 'factory' - an array of factory functions, one for 
/// each tag in the variadic template argument tags... 
/// Note that the array is zero-based, the tags may not be. All we care about 
/// here is the size of the list of tags (and their values) 
/// 
template<std::size_t...tags> 
constexpr auto make_factory(std::index_sequence<tags...>) 
{ 
    return std::array<i_value* (*)(), sizeof...(tags)> 
    { 
     &make_instance<static_cast<tag>(tags)>... 
    }; 
} 

// minimal memory manager 
struct memory_manager { 
    struct impl { 
     i_value* add(i_value* item) { 
      _ivalues.push_back(item); 
      return item; 
     }; 
     ~impl() { 
      for (auto i = _ivalues.rbegin() ; i != _ivalues.rend() ; ++i) { 
       delete *i; 
      } 
     } 
     std::deque<i_value*> _ivalues; 
    }; 
    static impl& instance() 
    { 
     static impl _instance = {}; 
     return _instance; 
    } 
}; 

// here is resulting factory function. 
i_value* make_empty_value(tag tag_type) 
{ 
    static constexpr auto factory = make_factory(all_tags()); 

    auto index = std::size_t(tag_type - tag::first); 
    if (index < tag::ntags) { 
     return memory_manager::instance().add(factory[index]()); 
    } 
    else { 
     return nullptr; 
    } 
} 

// test 
int main() 
{ 
    for(auto tag_type : { tag::ble, tag::chn }) 
    { 
     auto pvalue = make_empty_value(tag_type); 
     pvalue->prove(); 
    } 
} 

đầu ra mong đợi:

I have tag 0 
I have tag 1 
tag 1 destroyed 
tag 0 destroyed 
+0

Tôi có thể hỏi lý do tại sao bạn đã bỏ void() tại các hàm của t_value? Nó làm cho bất kỳ tốt hơn? – Jafffy

+0

Tin tưởng rằng nó có hay không, gói hoạt động ostream trong void ngăn xcode không đúng định dạng mã. Nó cũng xảy ra là một cách di động chỉ ra rằng bạn đang cố ý bỏ qua giá trị trả lại. –

0

Không trực tiếp sử dụng ví dụ của bạn, nhưng bạn có thể làm điều gì đó trên các dòng dưới đây, tức là chuyển đổi enum thành một loại.

enum Type { 
    Type_A, 
    Type_B, 
}; 

template <Type T> 
struct Enum2Type { 
    constexpr static const Type type = T; 
}; 

template <typename T> 
mfer::i_value* make_empty_value(T tag_type) 
{ 
    return memory_manager::instance().add(new mfer::t_value<tag_type.type>()); 
} 

auto val = make_empty_value(Enum2Type<Type_A>()); 
auto val2 = make_empty_value(Enum2Type<Type_B>()); 
+0

Nhưng nếu tôi sử dụng mã này, tôi không thể chuyển biến qua hàm này, phải không? – Jafffy

+0

Nếu bạn muốn tạo đối số của biến 'make_empty_value', thì không, bạn không thể. Mẫu cần một số giá trị mà nó có thể thấy thời gian biên dịch. Tại một số điểm, bạn sẽ phải sử dụng một trường hợp chuyển đổi nếu một tham số thời gian chạy của nó. – Arunmu

+0

Tôi hiểu. (Tôi mong đợi tình hình nên thực hiện theo cách đó sẽ được bình thường hơn, vì vậy tôi tin rằng có mô hình cho các mã số – Jafffy

1

Bạn có thể ánh xạ thẻ cho phương pháp nhà máy;

typedef std::unordered_map<mfer::tag,std::function<mfer::i_value*()>> TagMap; 

TagMap create_tag_map() 
{ 
    TagMap map; 

    map[mfer::tag::mwf_ble] = [](){ return new mfer::t_value<mfer::tag::mwf_ble>(); }; 
    map[mfer::tag::mwf_chn] = [](){ return new mfer::t_value<mfer::tag::mwf_chn>(); }; 
    map[mfer::tag::mwf_blk] = [](){ return new mfer::t_value<mfer::tag::mwf_blk>(); }; 
    //... 

    return map; 
} 

Phương pháp create_empty_value sau đó có thể trông như thế này:

mfer::i_value* make_empty_value(mfer::tag tag_) 
{ 
    static TagMap factory = create_tag_map(); 

    auto it = factory.find(tag_);  
    if(it != factory.end()) 
    { 
     return memory_manager::instance().add(it->second()); 
    } 

    return nullptr; 
} 

thấy phiên bản đơn giản Live on Coliru

+1

Nhưng sau đó nếu anh ta chỉ sử dụng điều này một lần, anh ta đang viết ngay cả * thêm * mã để tạo ra mẫu nhà máy cho một lần sử dụng. – CoffeeandCode

1

Bạn có thể tạo một mẫu hàm đệ quy nếu giá trị enumerate sau một mô hình nổi tiếng (theo giá trị liệt kê tiếp theo bằng với liệt kê trước đó +1):

//anonymous namespace to "help innliner" 
    namespace{ 
    //This function return the next enumerates value: 
    constexpr mref::tag next_tag(mref::tag tag_) { 
     return static_cast<mref::tag>(
      static_cast<std::underlying_type_t<mref::tag>>(tag_) + 1); 
    } 
    //The compute function is wrapped in a structure to enable template 
    //specialization: 
    template <mref::tag Tag> struct add_to_mem_manager { 
     static mfer::i_value* compute(mref::tag tag_) { 
     if (Tag == tag_) { 
      return memory_manager::instance().add(
       new mfer::t_value<Tag>()); 
     } else { 
      return add_to_mem_manager<next_tag(Tag)>::compute(tag_); 
     } 
     } 
    }; 
    //Specialization for last enumerate 
    template <> struct add_to_mem_manager<mfer::tag::mwf_pid> { 
     static mref::ivalue* compute(mref::tag tag_) { 
     assert(mref::tag::mwf_pid == tag_); 
     return memory_manager::instance().add(
       new mfer::t_value<mfer::tag::mwf_pid>()); 
     } 
    }; 
    } 
    mfer::i_value* make_empty_value(mfer::tag tag_){ 
    //call with template parameter equals to the 
    //the enumerate whose values is the smallest 
    return add_to_mem_manager<mfer::tag::mwf_ble>::compute(tag_); 
    } 

Nếu bạn không biết quy tắc cấu thành liệt kê, bạn không thể thực hiện điều này, (luật cấu thành chung như trong ví dụ này, x [i + 1] = x [i] +1 hoặc x [i + 1] = x [i] < < 1 (dịch chuyển trái).) Nếu không thì không có cách nào để lặp qua các phần tử của một liệt kê.

Lưu ý: Chức năng compute chắc chắn sẽ được sắp xếp theo hàng nhưng không chắc chắn bạn có thể sử dụng thuộc tính biên dịch cụ thể như __forceinline với MSVC hoặc __attribute__((__always_inline__)) với GCC hoặc kêu vang.

0

Phạm vi đơn giản hóa đơn giản nhất tôi thấy là xóa mã boilerplate bằng cách thay thế bằng macro cố định. Điều này sẽ làm dịu người xem.

Thay vì rất nhiều if-else if, làm cho nó một switch/case như sau:

#define CASE(TAG) \ 
    case TAG: return memory_manager::instance().add(new mfer::t_value<TAG>()) 

mfer::i_value* make_empty_value(const mfer::tag tag_) 
{ 
    switch(tag_) { 
    { 
    CASE(mfer::tag::mwf_ble); 
    CASE(mfer::tag::mwf_chn); 
    CASE(mfer::tag::mwf_blk); 
    //... 
    default: break; 
    } 
    return nullptr; 
} 

#undef CASE 
Các vấn đề liên quan