I have a class with a member variable std::function customCallback
, and I need to let the user customize its behavior. customCallback
should take as input a const std::array<int, N>&
and a int
, and return a std::array<int, N>
(where N
is a template parameter).
I am currently using std::bind
to achieve this, but I can't understand why the compilation fails. What am I missing?
Since the original code involves inheritance and templates, I am including inheritance and templates in my minimal reproducible example (live code here) as well.
The user should be able to use both the constructor OR a member function to set the custom behavior.
base.h
:
#include <array>
#include <functional>
template <std::size_t N>
class BaseClass {
public:
virtual std::array<int, N> CustomBehavior(const std::array<int, N>& user_array, int user_number) = 0;
protected:
std::array<int, N> my_array = {0, 0};
};
derived.h
:
#include <base.h>
#include <cstddef>
template <std::size_t N>
class DerivedClass : public BaseClass<N> {
public:
DerivedClass() = default;
DerivedClass(std::function<std::array<int, N>(const std::array<int, N>&, int)> custom_f)
: customCallback(std::bind(custom_f, std::ref(std::placeholders::_1), std::placeholders::_2)) {}
void SetCustomBehavior(std::function<std::array<int, N>(const std::array<int>&, int)> custom_f) {
customCallback = std::bind(custom_f, std::ref(std::placeholders::_1), std::placeholders::_2);
}
std::array<int, N> CustomBehavior(const std::array<int, N>& user_array, int user_number) override {
if (customCallback)
this->my_array = customCallback(user_array, user_number);
return this->my_array;
}
private:
std::function<std::array<int, N>(const std::array<int, N>&, int)> customCallback;
};
main.cpp
:
#include <derived.h>
#include <cassert>
static constexpr std::size_t MySize = 2;
std::array<int, MySize> my_behavior(const std::array<int, MySize>& input_array, int a) {
return {a * input_array[0], a * input_array[1]};
}
int main() {
std::array<int, MySize> my_array = {1, 1};
// Default constructor (COMPILES)
DerivedClass<MySize> foo_1; // OK
std::array<int, MySize> bar_1 = foo_1.CustomBehavior(my_array, 2);
assert(bar_1[0] == 0 && bar_1[1] == 0);
// Custom constructor (ERROR)
DerivedClass<MySize> foo_2(my_behavior); // COMPILATION ERROR
std::array<int, MySize> bar_2 = foo_2.CustomBehavior(my_array, 2);
assert(bar_2[0] == 2 && bar_2[1] == 2);
// Custom behavior set later on (ERROR)
DerivedClass<MySize> foo_3; // OK
foo_3.SetCustomBehavior(my_behavior); // COMPILATION ERROR
std::array<int, MySize> bar_3 = foo_3.CustomBehavior(my_array, 2);
assert(bar_3[0] == 2 && bar_3[1] == 2);
return 0;
}
I am not including the whole compilation error since it's fairly long, but it can be seen live code here.
Well, for starters, the first error you are getting is an error on std::array<int>
in Derived::SetCustomBehavior()
:
error: wrong number of template arguments (1, should be 2)
You also have an error on std::ref(std::placeholders::_1)
, it should be just std::placeholders::_1
.
Fixing those two mistakes, the code then compiles and runs (using corrected asserts):
That being said, you don't actually need std::bind()
at all in this situation. Your custom_f
parameter is the same type as your customCallback
member, so simply assign custom_f
as-is to customCallback
.
And do yourself a favor - create type aliases for your std::function
and std::array
types to make your code more readable.
Try this:
base.h
#include <array>
#include <functional>
#include <cstddef>
template<std::size_t N>
using intArray = std::array<int, N>;
template<std::size_t N>
using callbackType =
std::function<intArray<N>(const intArray<N>&, int)>;
template <std::size_t N>
class BaseClass {
public:
virtual intArray<N> CustomBehavior(const intArray<N>& user_array, int user_number) = 0;
protected:
intArray<N> my_array = {0, 0};
};
derived.h:
#include "base.h"
template <std::size_t N>
class DerivedClass : public BaseClass<N> {
public:
DerivedClass() = default;
DerivedClass(callbackType<N> custom_f)
: customCallback(std::move(custom_f))
{}
void SetCustomBehavior(callbackType<N> custom_f) {
customCallback = std::move(custom_f);
}
intArray<N> CustomBehavior(const intArray<N>& user_array, int user_number) override {
if (customCallback)
this->my_array = customCallback(user_array, user_number);
return this->my_array;
}
private:
callbackType<N> customCallback;
};
main.cpp:
#include "derived.h"
#include <cassert>
static constexpr std::size_t MySize = 2;
using myIntArray = intArray<MySize>;
myIntArray my_behavior(const myIntArray& input_array, int a) {
return {a * input_array[0], a * input_array[1]};
}
int main() {
myIntArray my_array = {1, 1};
// Default constructor
DerivedClass<MySize> foo_1;
myIntArray bar_1 = foo_1.CustomBehavior(my_array, 2);
assert(bar_1[0] == 0 && bar_1[1] == 0);
// Custom constructor
DerivedClass<MySize> foo_2(my_behavior);
myIntArray bar_2 = foo_2.CustomBehavior(my_array, 2);
assert(bar_2[0] == 2 && bar_2[1] == 2);
// Custom behavior set later on
DerivedClass<MySize> foo_3;
foo_3.SetCustomBehavior(my_behavior);
myIntArray bar_3 = foo_3.CustomBehavior(my_array, 2);
assert(bar_3[0] == 2 && bar_3[1] == 2);
return 0;
}