3

So here is the situation: I have two classes with static inheritance through CRTP. The base class has a run method that calls the derived method with a variadic template so that the arguments are flexible. Now the derived class contains a function object. The derived class has the implementation that is called by the base class. It may seem unnecessary but in the full version of this code more commands than just the contained function are run. Next there is a method that converts the function to a bool(void) function by binding all the variadic arguments, and instance to the the method CrtpBase::Run. This is where I am having an issue. I have tried two different approached, the version using a lambda is commented out. Neither method works. My goal is to have VoidFunction bind all the parameters so that I can execute the function at my leisure without the arguments. What am I doing wrong here?

#include <functional>
#include <utility>

template <typename D>
struct CrtpBase {
  template <typename ... Args>
  bool Run(Args&& ... args) const {
    return static_cast<D&>(*this).Impl(std::forward<Args>(args) ...);
  }
};

template <typename ... Args>
struct CrtpDerived : public CrtpBase<CrtpDerived<Args ...>> {
  CrtpDerived(std::function<bool(Args ...)> function) : runable(std::move(function)) {}

  bool Impl(Args&& ... args) const {
    return this->runable(std::forward<Args>(args) ...);
  }

  std::function<bool(Args ...)> runable;
};

template <typename D, typename ... Args>
std::function<bool()> VoidFunction(CrtpBase<D> base, Args&& ... args) {
//  return [&base, &args ...]()->bool{return CrtpBase<D>::template Run<Args ...>(base);};
  return std::bind(CrtpBase<D>::template Run<Args ...>, base, std::forward<Args>(args) ...);
}

int main(int argc, char** argv) {
  std::function<bool(int&)> fn = [](int& a)->bool{a /= 2; return (a % 2) == 1;};
  CrtpDerived<int&> derived(fn);
  int x = 7;
  auto voided = VoidFunction(derived, x);
  bool out = voided();
  if ((x == 3) and (out == true)) {
    return EXIT_SUCCESS;
  } else {
    return EXIT_FAILURE;
  }
}

Edits:

  1. Fixed typo in final test (out == false) became (out == true)
esdanol
  • 356
  • 3
  • 9

1 Answers1

1

Firstly, from the compiler's point of view CrtpBase<D>::template Run<Args ...> is a nonsensical/incomplete combination of tokens. There's no such expression syntax in C++. This looks like an attempt to form a pointer-to-member, but that requires an explicit application of & operator

return std::bind(&CrtpBase<D>::template Run<Args ...>, base, std::forward<Args> (args) ...);

Secondly, this cast

static_cast<D&>(*this)

will attempt to cast away constness. This is not allowed in static_cast.

Thirdly, your

std::bind(&CrtpBase<D>::template Run<Args ...>, base, std::forward<Args> (args) ...);

binds the implied this parameter to a function parameter base. This will not work, since the base will be destroyed as soon as VoidFunction exits (or as soon as the calling expression ends). As @aschepler correctly noted in the comments passing base in as CrtpBase<D> value sliced the original CrtpDerived<int&> object. Pass it in by reference and then use &base as argument for std::bind.

Fourthly, std::bind will not bind "by reference", and std::forward will not help you with this. This means that a inside your lambda fn will not be bound to x. Use std::ref to work around that limitation.

#include <functional>
#include <utility>

template <typename D>
struct CrtpBase {
  template <typename ... Args>
  bool Run(Args&& ... args) const {
    return static_cast<const D&>(*this).Impl(std::forward<Args>(args) ...);
  }
};

template <typename ... Args>
struct CrtpDerived : public CrtpBase<CrtpDerived<Args ...>> {
  CrtpDerived(std::function<bool(Args ...)> function) : runable(std::move(function)) {}

  bool Impl(Args&& ... args) const {
    return this->runable(std::forward<Args>(args) ...);
  }

  std::function<bool(Args ...)> runable;
};

template <typename D, typename ... Args>
std::function<bool()> VoidFunction(CrtpBase<D> &base, Args&& ... args) {
  return std::bind(&CrtpBase<D>::template Run<Args ...>, &base, std::forward<Args>(args) ...);
}

int main(int argc, char** argv) {
  std::function<bool(int&)> fn = [](int& a)->bool { a /= 2; return (a % 2) == 1; };
  CrtpDerived<int&> derived(fn);
  int x = 7;
  auto voided = VoidFunction(derived, std::ref(x));
  bool out = voided();
  if ((x == 3) && (out == false)) {
    return EXIT_SUCCESS;
  } else {
    return EXIT_FAILURE;
  }
}

One last thing: I don't understand why you expect your out to be false in the end.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • So, how then should I fix this? I can static cast to a `const D&`. That makes sense. It compiles. But when it runs it throws an instance of std::bad_function_call so there is still a problem somewhere. – esdanol Aug 27 '18 at 23:05
  • I am guessing that the std::bad_function_call that I'm getting is because of base getting destroyed. Why is that and how can I fix this? – esdanol Aug 27 '18 at 23:11
  • @esdanol: Yes, that is the reason. See corrected code above. It runs as expected, except that `out` is `true` in the end (as it should be). – AnT stands with Russia Aug 27 '18 at 23:13
  • Ah yes, that is a typo on my part. I will edit the code about and make a note of it in case somebody else looks at this post. Thank you very much! – esdanol Aug 27 '18 at 23:19
  • 1
    You mentioned `base` being destroyed, and corrected it to be a reference in the fixed code, but I would also point out the object slicing that happened with the non-reference base class variable. – aschepler Aug 27 '18 at 23:22
  • @aschepler: Yes, you are right. And my original explanation is incorrect. `std::bind` would store an internal copy of `base` preventing it from being destroyed prematurely. It is actually the slicing that causes the problems here. – AnT stands with Russia Aug 27 '18 at 23:32
  • What do you mean by slicing? – esdanol Aug 27 '18 at 23:45
  • @esdanol: https://stackoverflow.com/questions/274626/what-is-object-slicing. In your original code the first parameter of `VoidFunction` has type `CrtpBase`. And you are passing `CrtpDerived derived` as an argument, . What will happen is that a copy of `CrtpBase` subobject of `derived` will get passed, not the whole `derived` object (this is "slicing"). Later, trying to cast that `base` to `CrtpDerived` will simply lead to undefined behavior since that `base` is no longer a part of any `CrtpDerived`. – AnT stands with Russia Aug 27 '18 at 23:54