2017-08-01 69 views
4

如何處理爲相同輸入數據類型返回不同數據類型的api?如何處理爲相同輸入數據類型返回不同數據類型的api?

看下面的例子中,apicall應該返回日期或依賴於輸入屬性的字符串:

#include <iostream> 
#include <string> 

using namespace std; 

???? apicall(string datatype, string attribute) 
{ 
    // code 
} 

int main(int argc, char** argv) 
{ 
    string datatype = "Thomas" 
    string attribute = "bithday" 
    cout << apicall(datatype, attribute) << endl; 

    string datatype = "Thomas" 
    string attribute = "address" 
    cout << apicall(datatype, attribute) << endl; 
} 

有什麼能代替????apicall返回的數據類型),以及如何處理這些情況?

我想了解這些概念,因爲我迄今爲止的經驗已經與duck typed腳本語言。

+0

[確定基於輸入參數的返回類型]的可能的副本(https://stackoverflow.com/questions/11080343/determine-return-type-based-on-input-parameter) –

+0

@underscore_d這是基於*輸入參數的類型*,而不是*值*。 –

+0

正確,所以改爲標誌[返回類型基於輸入參數的值](https://stackoverflow.com/questions/18107012/return-type-based-on-value-of-input-parameter) –

回答

6

理想的解決方案是使用std::variant,這是一個安全的聯合型等。

這可以讓你寫:

using DateOrString = std::variant<DateType, std::string>; 

DateOrString api_call(std::string, std::string) { 
    // you can return both DateType and std::string 
} 

// ... 
auto result = api_call("", ""); 
auto& str = std::get<std::string>(result); 

不幸的是std::variantC++ 17功能。不過,不同的編譯器已經支持它。

如前所述,boost的類別爲variant,您可以將它與任何C++標準一起使用。


作爲最後一個選項,您可以實現一個處理日期和字符串的「變體」類。你的函數應該返回它。

Here演示如何快速實現那​​種類。

請注意,該類是安全的,因爲在運行時檢查類型。

作爲變種對象,你調用函數應該分支的類型,是這樣的:

auto result = api_call(/*...*/); 
if (result.is_string()) { 
    // result is a string 
    const auto& str = result.get_string(); 
} else { 
    // result is a date 
    const auto& date = result.get_date(); 
} 
2

您可以使用一個變體,但取決於調用者的網站來檢查結果。 Boost定義了兩個變體Boost.Variant和Boost.Any。

+0

在這種情況下,Boost(或「std」)變體可能更好,因爲Any可以保存的不僅僅是日期和字符串。 –

+2

你應該注意到這些類已經被修改到C++ 17的標準庫中。在這兩種情況下,也許還要鏈接到讀者可以參考的文檔。 –

6

...爲相同的輸入數據類型返回不同的數據類型?

這實際上是不可能的。一個函數用一個(或零)返回類型和零個或多個輸入參數類型來定義。

的解決方法是:

  1. 寫入單個函數返回一個變體類型,如在std::variant C++ 17,或Boost.Variant如果這是不可用的。
  2. 寫多個功能不同的返回類型(呼叫者只是有選擇的權利之一)
  3. 反轉控制,使而不是返回一個值,你通過能夠處理所有需要的類型的對象:

    struct APIHandler { 
        virtual ~APIHandler() {} 
        virtual void operator()(int) {} 
        virtual void operator()(string) {} 
    }; 
    void apicall(string name, string attr, APIHandler &h) { 
        // dummy implementation 
        if (attr == "address") { 
        h("123 Woodford Road"); 
        } else if (attr == "birthday") { 
        h(19830214); 
        } 
    } 
    
    // implement your type-specific logic here 
    struct MyHandler: APIHandler { 
        void operator()(int i) override { 
        cout << "got an int:" << i << '\n'; 
        } 
        void operator()(string s) override { 
        cout << "got a string:" << s << '\n'; 
        } 
    }; 
    
    // and use it like: 
    MyHandler mh; 
    apicall("Thomas", "birthday", mh); 
    apicall("Thomas", "address", mh); 
    
+0

這是一個很好的方法,類似於「訪問」一個變體,但沒有變體中介。我想APIHander是一個模板參數,所以我們可以使用通用lambda或其他非虛擬實現。但是這很好地提供了這個想法。 –

2

你想在C++ 17 std::variantboost::variant或推出自己的原油變​​種類似這樣的:

constexpr std::size_t max() { return 0; } 
template<class...Ts> 
constexpr std::size_t max(std::size_t t0, Ts...ts) { 
    return (t0<max(ts...))?max(ts...):t0; 
} 
template<class T0, class...Ts> 
struct index_of_in; 
template<class T0, class...Ts> 
struct index_of_in<T0, T0, Ts...>:std::integral_constant<std::size_t, 0> {}; 
template<class T0, class T1, class...Ts> 
struct index_of_in<T0, T1, Ts...>: 
    std::integral_constant<std::size_t, 
     index_of_in<T0, Ts...>::value+1 
    > 
{}; 

struct variant_vtable { 
    void(*dtor)(void*) = 0; 
    void(*copy)(void*, void const*) = 0; 
    void(*move)(void*, void*) = 0; 
}; 
template<class T> 
void populate_vtable(variant_vtable* vtable) { 
    vtable->dtor = [](void* ptr){ static_cast<T*>(ptr)->~T(); }; 
    vtable->copy = [](void* dest, void const* src){ 
    ::new(dest) T(*static_cast<T const*>(src)); 
    }; 
    vtable->move = [](void* dest, void* src){ 
    ::new(dest) T(std::move(*static_cast<T*>(src))); 
    }; 
} 
template<class T> 
variant_vtable make_vtable() { 
    variant_vtable r; 
    populate_vtable<T>(&r); 
    return r; 
} 
template<class T> 
variant_vtable const* get_vtable() { 
    static const variant_vtable table = make_vtable<T>(); 
    return &table; 
} 
template<class T0, class...Ts> 
struct my_variant { 
    std::size_t index = -1; 
    variant_vtable const* vtable = 0; 
    static constexpr auto data_size = max(sizeof(T0),sizeof(Ts)...); 
    static constexpr auto data_align = max(alignof(T0),alignof(Ts)...); 
    template<class T> 
    static constexpr std::size_t index_of() { 
     return index_of_in<T, T0, Ts...>::value; 
    } 
    typename std::aligned_storage< data_size, data_align >::type data; 
    template<class T> 
    T* get() { 
    if (index_of<T>() == index) 
     return static_cast<T*>((void*)&data); 
    else 
     return nullptr; 
    } 
    template<class T> 
    T const* get() const { 
    return const_cast<my_variant*>(this)->get<T>(); 
    } 
    template<class F, class R> 
    using applicator = R(*)(F&&, my_variant*); 
    template<class T, class F, class R> 
    static applicator<F, R> get_applicator() { 
    return [](F&& f, my_variant* ptr)->R { 
     return std::forward<F>(f)(*ptr->get<T>()); 
    }; 
    } 
    template<class F, class R=typename std::result_of<F(T0&)>::type> 
    R visit(F&& f) & { 
    if (index == (std::size_t)-1) throw std::invalid_argument("variant"); 
    static const applicator<F, R> table[] = { 
     get_applicator<T0, F, R>(), 
     get_applicator<Ts, F, R>()... 
    }; 
    return table[index](std::forward<F>(f), this); 
    } 
    template<class F, 
    class R=typename std::result_of<F(T0 const&)>::type 
    > 
    R visit(F&& f) const& { 
    return const_cast<my_variant*>(this)->visit(
     [&f](auto const& v)->R 
     { 
     return std::forward<F>(f)(v); 
     } 
    ); 
    } 
    template<class F, 
    class R=typename std::result_of<F(T0&&)>::type 
    > 
    R visit(F&& f) && { 
    return visit([&f](auto& v)->R { 
     return std::forward<F>(f)(std::move(v)); 
    }); 
    } 
    explicit operator bool() const { return vtable; } 
    template<class T, class...Args> 
    void emplace(Args&&...args) { 
    clear(); 
    ::new((void*)&data) T(std::forward<Args>(args)...); 
    index = index_of<T>(); 
    vtable = get_vtable<T>(); 
    } 
    void clear() { 
    if (!vtable) return; 
    vtable->dtor(&data); 
    index = -1; 
    vtable = nullptr; 
    } 
    ~my_variant() { clear(); } 

    my_variant() {} 
    void copy_from(my_variant const& o) { 
    if (this == &o) return; 
    clear(); 
    if (!o.vtable) return; 
    o.vtable->copy(&data, &o.data); 
    vtable = o.vtable; 
    index = o.index; 
    } 
    void move_from(my_variant&& o) { 
    if (this == &o) return; 
    clear(); 
    if (!o.vtable) return; 
    o.vtable->move(&data, &o.data); 
    vtable = o.vtable; 
    index = o.index; 
    } 
    my_variant(my_variant const& o) { 
    copy_from(o); 
    } 
    my_variant(my_variant && o) { 
    move_from(std::move(o)); 
    } 
    my_variant& operator=(my_variant const& o) { 
    copy_from(o); 
    return *this; 
    } 
    my_variant& operator=(my_variant&& o) { 
    move_from(std::move(o)); 
    return *this; 
    } 
    template<class T, 
    typename std::enable_if<!std::is_same<typename std::decay<T>::type, my_variant>{}, int>::type =0 
    > 
    my_variant(T&& t) { 
    emplace<typename std::decay<T>::type>(std::forward<T>(t)); 
    } 
}; 

那麼你的代碼如下所示:

variant<string, int> apicall(string datatype, string attribute) 
{ 
    if (datatype > attribute) return string("hello world"); 
    return 7; 
} 

int main() 
{ 
    string datatype = "Thomas" 
    string attribute = "bithday" 
    apicall(datatype, attribute).visit([](auto&&r){ 
    cout << r << endl; 
    }); 
    string datatype = "Thomas" 
    string attribute = "address" 
    apicall(datatype, attribute).visit([](auto&& r){ 
    cout << r << endl; 
    }); 
} 

visitapply_visitor免費功能或方法您的特定variant支持。

由於我們沒有通用的lambda表達式,這使得C++ 11變得更加惱人。

相關問題