函数注册与动态组合(4)

fnode 的实现

对于 fnode 来说,不少东西需要通过 boost::function_traits 来获得(STL 居然没有!),比如返回类型,参数个数乃至每个参数的类型。如此一来我们可以对 input_count、output_count、input_type、output_type 这些辅助函数提供简单的实现。比如我们甚至可以为返回类型进行特化,当 type 为 std::pair、std::tuple 的时候进行 unpack 计算实际的参数个数,只是实现 int 到对应位置的关系的时候我们需要建立一个 static 的映射关系。

namespace internal {
  template <typename T>
  struct num_elem {
    static const int size = 1;
  };
  
  template <typename K, typename V>
  struct num_elem<std::pair<K, V>> {
    static const int size = num_elem<K>::size + num_elem<V>::size;
  };

  template <typename T>
  struct num_elem<std::tuple<T>> {
    static const int size = num_elem<T>::size;
  };

  template <typename T, typename... Args>
  struct num_elem<std::tuple<T, Args...>> {
    static const int size = num_elem<T>::size +
        num_elem<std::tuple<Args...>>::size;
  };

  template <typename T, int I>
  struct elem {
    using type = T;
  };

  template <typename K, typename V, int I>
  struct elem<std::pair<K, V>, I> {
    using type = typename std::conditional<
      (I < num_elem<K>::size),
      typename elem<K, I>::type,
      typename elem<V, I-num_elem<K>::size>::type
      >::type;
  };

  template <typename T, int I>
  struct elem<std::tuple<T>, I> {
    using type = typename elem<T, I>::type;
  };

  template <typename T, int I, typename... Args>
  struct elem<std::tuple<T, Args...>, I> {
    using type = typename std::conditional<
      (I < num_elem<T>::size),
      typename elem<T, I>::type,
      typename elem<std::tuple<Args...>, I-num_elem<T>::size>::type
      >::type;
  };
}

template <typename T>
struct unpacked {
  static const int size = internal::num_elem<T>::size;
  
  template <int I>
  struct elem : internal::elem<T, I> {};
};

类似的想法我们甚至可以为这种结构设计一个 accessor 用来访问任意元素(估计这里面有个诡异的 specialization 的 bug)。

连接的建立

我们一直回避了一个重要的 API,那就是在两个 node 进行连接的时候如何处理。在没有 compiler magic 的情况下,我们可以简单的做一个 type check。假定函数返回值由 fnode 本身存放,我们通过 unpacked::<fnode:value_type>::get<I> 可以获得对应输出元素的值,这时我们就会需要 compile-time 和 runtime 的 int 的转换,但是 get<I> 返回的类型并不一致,这就要求我们处理 type erasure 的事情,比如利用 boost.any

struct connection {
  node* source;
  int out;

  boost::any get() {
    return node->output_ptr(out);
  }
};
template <typename Func, Func func>
struct fnode {
  using traits = function_traits<Func>;
  using value_type = result_type;

  boost::any output_ptr(int out) {
    static const auto ops = internal::accessor_to_any_ptr<fnode<Func, func>::output>();
    if (out < 0 || out >= output_count) {
      throw std::runtime_error("boundary problem");
    }
    return ops[out](value);
  }

  // Connects this->input(in) with another.output(out)
  node& connect(node& another, int in, int out) {
    assert(this->input_type(in) == another.output_type(out));
    conn[in] = connection{&another, out};
  }

  std::unique_ptr<value_type> value;  // value_type has to be movable or copiable 
  std::vector<connection> conn;
};

我们这里可以利用前文的技术实现 runtime int 到 compile-time int 的映射。通过保留一组 connection 将在运行的时候拿到对应的 point 并将其 de-reference 出来即可。

Advertisements
函数注册与动态组合(4)

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s