I'm trying to summon an elder demon, and this is one of the trials before me.
I want a class which will take, as a template parameter, or at least a construction variable, the address to a data member of a class. I will later provide an instance of that class so the template class knows where to perform a small aspect of the summoning.
This is what I have so far, but wrong results:
#include <functional>
#include <iostream>
struct B
{
int foo;
bool bar;
};
template<auto M>
class A
{
public:
typedef std::function<bool(decltype(std::declval<B>().*M)&)> Callback;
A(Callback callback)
:
m_callback(callback)
{}
bool X
(
B& b
) const
{
if(m_callback(m_cache))
{
b.*M = m_cache;
return true;
}
return false;
}
protected:
Callback m_callback;
mutable typename std::remove_cvref<decltype(std::declval<B>().*M)>::type m_cache;
};
using FooA = A<&B::foo>;
using BarA = A<&B::bar>;
int main()
{
FooA fooA([](int& f) { std::cout << "foo " << f << std::endl; ++f; return true; });
BarA barA([](bool& b) { std::cout << "bar " << b << std::endl; b = !b; return true; });
B b{.foo = 1, .bar = false};
fooA.X(b);
barA.X(b);
fooA.X(b);
barA.X(b);
return 0;
};
output:
foo 0
bar 120
foo 1
bar 121
As @Miles Budnek noticed, m_cache
is not initialized which explains why you get wrong results (note that valgrind is your friend for tracking such an issue).
You can also define a shortcut type member_t
that makes the whole thing easier to read:
#include <functional>
#include <iostream>
struct B
{
int foo;
bool bar;
};
template<auto M>
class A
{
public:
using member_t = std::remove_cvref_t<decltype(std::declval<B>().*M)>;
typedef std::function<bool(member_t&)> Callback;
A(Callback callback) : m_callback(callback) {}
bool X (B& b) const
{
if(m_callback(m_cache))
{
b.*M = m_cache;
return true;
}
return false;
}
protected:
Callback m_callback;
mutable member_t m_cache = {}; // initialization
};
using FooA = A<&B::foo>;
using BarA = A<&B::bar>;
int main()
{
FooA fooA([](int& f) { std::cout << "foo " << f << std::endl; ++f; return true; });
BarA barA([](bool& b) { std::cout << "bar " << b << std::endl; b = !b; return true; });
B b{.foo = 1, .bar = false};
fooA.X(b);
barA.X(b);
fooA.X(b);
barA.X(b);
return 0;
};
Note: since you need std::remove_cvref_t
, you should use the tag c++20
for your post.