如何处理为相同的输入数据类型返回不同数据类型的api?

时间:2017-08-01 15:41:43

标签: c++ function c++11 types c++14

如何处理为相同输入数据类型返回不同数据类型的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脚本语言的经验。

4 个答案:

答案 0 :(得分:6)

  

...为相同的输入数据类型返回不同的数据类型?

这实际上是不可能的。函数定义为一个(或零)返回类型,以及零个或多个输入参数类型。

解决方法是:

  1. 编写一个返回变体类型的函数,例如{+ 3}}在C ++ 17中,或std::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);
    

答案 1 :(得分: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::variant C ++ 17 功能。但是不同的编译器已经支持它了。

正如已经建议的那样,boost有一个variant类,您可以将它与任何 C ++标准一起使用。

作为最后一个选项,您可以实现类似&#34;变体&#34;处理日期和字符串的类。你的函数应该返回它。

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 :(得分:2)

您可以使用变体,但是由调用者站点来检查结果。 Boost和std定义了两种变体类型,即std::variantstd::any

答案 3 :(得分:2)

你想在C ++ 17或std::variant中使用boost::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;
  });
}

使用您的特定visit支持的apply_visitorvariant免费功能或方法。

这在C ++ 11中变得更加烦人,因为我们没有通用的lambda。