如何在一个容器中存储各种类型的回调函数

时间:2018-12-08 19:11:36

标签: c++ c++11 opencl

我需要存储从opencl到以后执行的回调函数,例如:

void pfn_notify(const char *errinfo, const void *private_info, size_t cb, void *user_data){
    fprintf(stderr, "OpenCL Error (via pfn_notify): %s\n", errinfo);
}

这些回调函数各不相同,因此我想统一存储方法并将指向它们的指针存储在某种容器(vectormap等)中。另外,在存储后需要传递一些参数,而不是在将其推入容器时进行绑定。

  1. 常规伪代码方案:

    ...some code in main...
    Client.storecallback(function_to_store(some_arguments));
    ...rest code in main...
    
    class Client{
      void storecallback(void*(*)(some_arguments) function){
        callback_thread.store(function);
        callback_thread.start();
      }
    
      CallbackThread callback_thread;
    };
    
    class CallbackThread() {
      void start(){
       /* make connection via tcp/ip */
       receive();
      }
      void store(void*(*) function){
        callback.store(key, function);
      }
    
      void receive() {
        Buffer* input = new Buffer(tcp_socket);
        int a = input->pull();
        char b = input->pull();
        callback.raise(key, a, b);
      }
    
      Callback callback;
    };
    
    class Callback {
      std::map<uint64_t key, void*(*) function> callback_map;
      void store(void*(*) function){
        callback_map[key] = function;
      }
      template<typename... Args>
      void raise(uint64_t key, Args... rest_of_arguments){
        callback_map[key](rest_of_arguments);
      }
    };
    

我知道我需要统一的额外类,例如Functor类。

  1. 使用std::functionstd::bind可以将存储的变量类型统一为std::function<void()>,但是,我无法将新参数更改/绑定到回调函数。此解决方案需要通用struct,该通用 namespace project_name { namespace detail { template<int I> struct placeholder {}; } } namespace std { template<int I> struct is_placeholder< project_name::detail::placeholder<I> > : std::integral_constant<int, I>{}; } namespace project_name { namespace detail { template <size_t... Is> struct indices {}; template <size_t N, std::size_t... Is> struct build_indices : build_indices<N - 1, N - 1, Is...> {}; template <size_t... Is> struct build_indices<0, Is...> : indices<Is...> {}; template<std::size_t... Is, class F, class... Args> inline auto easy_bind(indices<Is...>, F const& f, Args&&... args) -> decltype(std::bind(f, std::forward<Args>(args)..., placeholder<Is + 1> {}...)){ return std::bind( f, std::forward<Args>(args)..., placeholder<Is + 1> {} ...); } } class Functor { public: template<class R, class... FArgs, class... Args> Functor(std::function<R(FArgs...)> f, Args&&... args) {} template<class R, class... FArgs, class... Args> static inline auto easy_bind(std::function<R(FArgs...)> f, Args&&... args) -> decltype(detail::easy_bind( detail::build_indices<sizeof...(FArgs) - sizeof...(Args)> {}, f, std::forward<Args>(args)...)) { return detail::easy_bind( detail::build_indices<sizeof...(FArgs) - sizeof...(Args)> {}, f, std::forward<Args>(args)...); } } 将存储指向参数变量的指针,并在调用存储函数之前用数据替换/填充数据,除了为每个回调函数的结构模板之外,我不知道如何创建这样的通用结构对我来说不是一个很好的解决方案。

  2. 借助这个Indicies和这个Stackoverflow,我能够创建一个解决方案,在该解决方案中,我可以使用占位符统一创建过程,从而可以在调用时添加一些参数,不仅存储:

    std::placeholder

不幸的是,由于使用public static void main(String[] args) throws FileNotFoundException, IOException { String sourceFilePath = "C://MyData/Emp_Input.csv"; String newFilePath = "C://MyData/Emp_Output.csv"; File sourceCsvFile = new File(sourceFilePath); File finalCsvFile = new File(newFilePath); final Charset Encoding = Charset.forName("UTF-8"); String cvsSplitBy = ","; String line = ""; BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(finalCsvFile), Encoding)); try (BufferedReader br = new BufferedReader(new FileReader(sourceCsvFile))) { line = br.readLine(); String newFileLine = "UniqueID" + cvsSplitBy +line + cvsSplitBy + "IsDelete" + cvsSplitBy + "Rootpid"; writer.write(newFileLine); writer.newLine(); while ((line = br.readLine()) != null) { /* if (line.contains("") ) { line = line.replace("", "NULL"); // missing code to replace empty cell of a csv with text as NULL } */ else if (line.contains("TRUE") || line.contains("FALSE")) { line = line.replace("TRUE", "1"); line = line.replace("FALSE", "0"); } newFileLine = cvsSplitBy + line + cvsSplitBy + cvsSplitBy; writer.write(newFileLine); writer.newLine(); } writer.close(); } } 时返回的类型不同,现在我不知道如何统一存储它。

这是我的问题:

    这两种方法中哪个更好,以及如何解决该方法的问题。
  1. 也许我应该考虑其他方法,但是由于缺乏知识而没有想到。

编辑: 您的评论带有与甚至不是我的问题都差不多的问题的线程链接绝对是没有用的。在链接我已经实现并插入到我的问题中的内容之前,请先阅读有关什么问题的信息。无论如何解决了,我会尽快添加解决方案。

1 个答案:

答案 0 :(得分:0)

好的,有点晚了,但是我想确定一下,圣诞节假期也发生了。这是Functors的模板化类的完整示例,可以稍后存储在map中并使用各种参数轻松调用。这就是我在谈论的内容,我敢肯定除了Some programmer dude之外,没有人提供任何有用的建议(可能是由于我的解释不力)。我们在这里使用了一些花哨的技巧(大部分由堆栈溢出和文档提供)。

这是保存所有回调的类的草图,我们通过将统一的回调函数重构为std::shared_ptr<void>来存储它们。这是必需的,因为它执行类型擦除。这使我们可以将所有参数存储在一个映射中,而与参数类型无关。但是,将使用内部存储的正确deleter来调用指针析构函数。更多信息:Stackoverflow std::shared_ptr

class Event {
 public:
  template<typename... Args>
  size_t addFunction(uint64_t callback_id, void(*callback)(Args...), Args... args) {
    std::shared_ptr<Functor<Args...>> ptr(new Functor<Args...>(this->p_input, callback, args...));

    std::map<uint64_t, std::shared_ptr<void>>::const_iterator it = p_functionMap.find(callback_id);

    if (it == p_functionMap.end()) {
      p_functionMap[callback_id] = ptr;

      return ++p_functionIndex;
    } else {
      std::cout << "WE HAVE THIS CALLBACK ALREADY!: " << callback_id << std::endl;
    }
    return p_functionIndex;
  }

  size_t removeFunction(uint64_t callback_id) {
    p_functionIndex -= p_functionMap.erase(callback_id);
    return p_functionIndex;
  }

 private:
  explicit Event(Buffer* input) :
  p_input(input) {
    p_functionIndex = 0;
  }

 private:
  std::atomic<size_t> p_functionIndex;
  std::map<uint64_t, std::shared_ptr<void>> p_functionMap;
  Buffer* p_input;
};

这是模板类的草图,其中包含有关函子的所有必要信息。我们使用参数包来避免定义参数类型。参数存储在std::tuple中,这使我们可以稍后在调用回调函数时使用它们,也可以将它们与新函数交换(例如,OpenCL回调)。对参数的每个基本操作都可以在析构函数中执行。在析构函数中引发回调之后,好吧,这就是所有人!更多信息:Stackoverflow unpack parameter packs to call function pointer

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
swapArguments(std::tuple<Tp...>& t, cccl::Buffer* input) { }

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if < I < sizeof...(Tp), void>::type
swapArguments(std::tuple<Tp...>& t, cccl::Buffer* input) {
  using ARG = std::remove_reference<decltype(std::get<I>(t))>::type;

  /*
    HERE NEW TUPLE AND SWAP ARGUMENTS
    OR ANYTHING ELSE(FOR EXAMPLE BUFFER DATA
    LIKE BELOW)
  */

  std::get<I>(t) = p_input->pull<ARG>();
  swapArguments<I + 1, Tp...>(t, input);
}

template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> {};
template<int ...S> struct gens<0, S...> { typedef seq<S...> type; };

template<typename... Args>
class Functor {
  std::tuple<Args...> arguments;
  void(*callback)(Args...);
  Buffer *p_input;

public:
 void dispatchCallback()
 {
    return callbackFunction(typename gens<sizeof...(Args)>::type());
 }

 template<int ...S>
 void callbackFunction(seq<S...>)
 {
   return this->callback(std::get<S>(this->arguments) ...);
 }

 Functor(Buffer *input, void(*callback)(Args...), Args... args) {
   this->p_input = input;
   this->arguments = std::make_tuple(args...);
   this->callback = callback;

 }

 ~Functor() {
    swapArguments(this->arguments, this->p_input);
    this->dispatchCallback();
 }
};

我希望我已经正确解释了所有内容。如果需要更详细的描述,请告诉我,我将尝试扩大答案。新年快乐!