2011-12-20 29 views
6

Sử dụng C++, tôi đang cố gắng tạo một lớp chứa chung để xử lý nhiều kiểu dữ liệu. Đó là một vấn đề phổ biến với nhiều giải pháp khác nhau, nhưng tôi không tìm thấy gì ... trực quan như tôi đã quen với các ngôn ngữ như Python hoặc thậm chí VB/VBA ...Vùng chứa chung cho nhiều kiểu dữ liệu trong C++

Vì vậy, đây là kịch bản của tôi:

Tôi đã xây dựng một lớp DataContainer dựa trên boost :: bất kỳ thứ gì tôi sử dụng để lưu trữ nhiều loại dữ liệu của nhiều phần tử. Tôi sử dụng một bản đồ như tuyên bố:

std::map<std::string, DataContainer* (or DataContainerBase*)> 

nơi DataContainer là một lớp mà đóng gói một đối tượng của loại:

std::list<boost::any> 

cùng với các chức năng thuận tiện cho việc quản lý/truy cập vào danh sách.

Tuy nhiên, cuối cùng, tôi vẫn buộc phải thực hiện chuyển đổi loại bên ngoài vùng chứa dữ liệu.

Ví dụ, nếu tôi được lưu trữ một danh sách các int giá trị trong bản đồ, truy cập vào chúng sẽ yêu cầu:

int value = boost::any_cast<int>(map["myValue"]->get()); 

Tôi thà mã tăng được chứa hoàn toàn trong cấu trúc chứa dữ liệu, do tôi sẽ chỉ cần loại:

int value = map["myValue"]->get(); 

hoặc, trường hợp xấu nhất:

int value = map["myValue"]->get<int>(); 

Tất nhiên, tôi đồng ULD liệt kê các kiểu dữ liệu của tôi và làm điều gì đó như:

int value = map["myValue"]->get(TYPE_INT); 

hoặc viết loại cụ thể get() chức năng:

getInt(), getString(), getBool() ... 

Vấn đề với hai lựa chọn cuối cùng là họ có phần cứng nhắc, đòi hỏi tôi để khai báo rõ ràng từng loại tôi muốn lưu trữ trong vùng chứa. Các giải pháp any_cast (mà tôi đã thực hiện và hoạt động) Tôi giả sử là tốt, nó chỉ là ... inelegant? Tôi không biết. Có vẻ như tôi cũng không nên sử dụng cơ chế bên trong nữa.

Như tôi thấy, chuyển giá trị mà không khai báo loại giá trị trong cuộc gọi đến hàm thành viên DataContainer sẽ yêu cầu giải pháp void * (không mong muốn vì lý do rõ ràng) và sử dụng cuộc gọi "get()" yêu cầu (cho đến nay như tôi có thể nói) một chức năng thành viên "mẫu ảo" được định nghĩa ở cấp độ cơ sở, trong đó, tất nhiên, không được phép.

Vì vậy, tôi có giải pháp khả thi và thực sự, việc sử dụng của tôi trong trường hợp này bị giới hạn trong phạm vi mà hầu hết mọi giải pháp đều hoạt động tốt. Nhưng tôi tự hỏi nếu có lẽ có một cách linh hoạt hơn để quản lý một thùng chứa dữ liệu chung, nhiều loại hơn thế này.

+1

Có một cái nhìn về 'boost :: variant' quá, có lẽ đây là những gì bạn đang tìm kiếm (?). – Kos

+0

Có gì sai với chỉ 'std :: map '? –

+0

Ngoài ra, vì 'DataContainer' của bạn nằm dưới sự kiểm soát của bạn, tại sao không chỉ thêm một hàm thành viên' template get_as() {return boost :: any_cast (get()); } 'để bọc bất kỳ diễn viên? Sau đó, bạn có thể nói 'm [" abc "] -> get_as ()', như bạn đã đề xuất. Âm thanh đủ đơn giản. –

Trả lời

6

Nếu bạn muốn giới thiệu một số đường cho việc này:

int value = boost::any_cast<int>(map["myValue"]->get()); 

sau đó bạn có thể muốn làm cho get() hàm trả về một đối tượng proxy, định nghĩa + - như thế này:

struct Proxy { 
    boost::any& value; 
    Proxy(boost::any& value) : value(value) {} 

    template<typename T> 
    operator T() { 
     return boost::any_cast<T>(value); 
    } 
}; 

Sau đó, cú pháp này sẽ hoạt động:

int value = map["myValue"]->get(); 
// returns a proxy which gets converted by any_cast<int> 

Tuy nhiên tôi khuyên bạn nên giữ mọi thứ rõ ràng và chỉ sử dụng cú pháp rằng:

int value = map["myValue"]->get<int>(); 

Đây get không trả lại một đối tượng proxy với một phương pháp mẫu, nhưng là một phương pháp mẫu tự (nhưng không giống như các nhà điều hành mẫu chuyển đổi hiển thị ở trên).

+0

@ JoelGraff Nếu tôi không tắt bởi nhiều bạn thậm chí có thể nhận được ngay với chỉ 'get()' trong trường hợp thứ hai nhờ vào khấu trừ tham số mẫu tự động. bạn có thể xem xét để lưu trữ DataCOntainer trực tiếp để tiết kiệm một mức độ của indirection. Một danh sách ngay lập tức shoudl chính nó là khá nhỏ (chỉ có đầu), các yếu tố trên heap anyway. – ted

+0

Tôi tin rằng khấu trừ tham số tự động chỉ hoạt động đối với các tham số chứ không phải loại trả về. – Kos

+0

bạn đúng, hãy xem http://stackoverflow.com/a/2612979/258418 này, nó cũng chỉ ra cách để loại bỏ một loại thông qua cách giải quyết, tuy nhiên điều này chỉ thú vị đối với các loại khá phức tạp – ted

1

Hôm nay tôi đã thực hiện một số mã nguồn cho mục đích bạn muốn. Tôi biết rằng câu hỏi này quá cũ, nhưng có lẽ đoạn mã nhỏ này rất hữu ích cho ai đó. Nó chủ yếu dựa trên tăng: bất kỳ.

/* 
* AnyValueMap.hpp 
* 
* Created on: Jun 3, 2013 
*  Author: alvaro 
*/ 

#ifndef ANYVALUEMAP_HPP_ 
#define ANYVALUEMAP_HPP_ 

#include <map> 
#include <boost/any.hpp> 

using namespace std; 

template <class T> 
class AnyValueMap { 

public: 
    AnyValueMap(){} 

    virtual ~AnyValueMap(){} 

private: 
    map<T, boost::any> container_; 

    typedef typename map<T, boost::any>::iterator map_iterator; 
    typedef typename map<T, boost::any>::const_iterator map_const_iterator; 

public: 

    bool containsKey(const T key) const 
    { 
     return container_.find(key) != container_.end(); 
    } 

    bool remove(const T key) 
    { 
     map_iterator it = container_.find(key); 
     if(it != container_.end()) 
     { 
      container_.erase(it); 
      return true; 
     } 
     return false; 
    } 

    template <class V> 
    V getValue(const T key, const V defaultValue) const 
    { 
     map_const_iterator it = container_.find(key); 
     if(it != container_.end()) 
     { 
      return boost::any_cast<V>(it->second); 
     } 
     return defaultValue; 
    } 

    template <class V> 
    V getValue(const T key) const 
    { 
     return boost::any_cast<V>(container_.at(key)); 
    } 

    template <class V> 
    void setValue(const T key, const V value) 
    { 
     container_[key] = value; 
    } 
}; 

#endif /* ANYVALUEMAP_HPP_ */ 

Một đơn giản sử dụng ví dụ:

AnyValueMap<unsigned long> myMap; 
myMap.setValue<double>(365, 1254.33); 
myMap.setValue<int>(366, 55); 
double storedDoubleValue = myMap.getValue<double>(365); 
int storedIntValue = myMap.getValue<int>(366); 
Các vấn đề liên quan