ReactiveX / RxCpp

Reactive Extensions for C++
Apache License 2.0
3.07k stars 396 forks source link

Function template specialization for operators fails #569

Closed thorstink closed 2 years ago

thorstink commented 2 years ago

Hi,

First off all, I'm not sure if this is an issue, nut I also haven't figured out where to ask rxcpp questions.. So feel free to close if considered not an issue.

I was trying to get function specialization working for functions passed to operators. This way I need to change less code when rewiring operators that merge streams, it'd just be limited to making sure the functions are properly specialized.

Below is a minimal example to demonstrate what's working and what I think I want to achieve.

#include <rxcpp/rx.hpp>
using namespace rxcpp;

template <typename... Args> bool func1(const std::tuple<Args...> &); // this would be in a header
template <> bool func1(const std::tuple<int, int> &t) { return true; } // and I could have many of these.

int main() {
  auto o1 = observable<>::just(1);
  auto o2 = observable<>::just(1);

  // works, but want to avoid explicit specialization.
  o1.combine_latest(o2).map(&func1<int, int>).subscribe();
  // works, without explicit specialization, can I get rid of the lambda?
  o1.combine_latest(o2).map([](const auto &s) { return func1(s); }).subscribe();
  // doesn't compile.. But is there a workaround? I couldn't find any..
  //   o1.combine_latest(o2).map(&func1).subscribe();
}

The last line is what I would like to achieve. But at the moment I'm not sure I can given RxCpp's implmentation. Can someone confirm? (Or better, if somebody knows how to it'd be happy to learn ).

Note that I also experimented by specializing a variadic function directly to the combine_latest-operator, but also that didn't work out.

I'm using GCC-9 and compiling with cpp17 flag.

victimsnino commented 2 years ago

Problem is that actually you can't obtain pointer to template (and not specialized) function. So, best way would be to create proxy-functor for this one. Something like this

struct ProxyCallFunc1
{
template<typename T>
bool operator()(T&& v) const {return func1(std::forward<T>(v));
}

...
...

1.combine_latest(o2).map(ProxyCallFunc1{}).subscribe();

As a result you can pass it into map without specialization and it would accept any type and forward it without any additional copies/moves

thorstink commented 2 years ago

Hi! I have to investigate/learn to see why I can't obtain the pointer, but I believe you :-) I also have to read up upon forwarding. So much to learn.. so little time!

Your solution seems closest to what I can get, thanks a lot. I'll close this!

#include <rxcpp/rx.hpp>
using namespace rxcpp;

bool func1(const std::tuple<int, int> &t) {
  return true;
}; 
bool func1(const std::tuple<int, int, int> &t) {
  return true;
}; 

struct ProxyCallFunc1_t {
  template <typename T> bool operator()(T &&v) const {
    return func1(std::forward<T>(v));
  }
};

constexpr ProxyCallFunc1_t ProxyCallFunc1;

int main() {
  auto o1 = observable<>::just(1);
  auto o2 = observable<>::just(1);
  auto o3 = observable<>::just(1);

  // one way
  o1.combine_latest(o2).map([](const auto &s) { return func1(s); }).subscribe();
  o1.combine_latest(o2, o3).map([](const auto &s) { return func1(s); }).subscribe();
  // cooler/nicer thanks :D 
  o1.combine_latest(o2).map(ProxyCallFunc1).subscribe();
  o1.combine_latest(o2, o3).map(ProxyCallFunc1).subscribe();
}