I would like to be able to 'forward' a member function call of a class to every member variable of the class:
class MyObject {
X_Behavior v1;
X_Behavior v2;
...
Y_Behavior v10;
Z_Behavior v11;
...
public:
void clear() { v1.clear(); v2.clear(); ... v10.clear(); v11.clear(); }
void hide() { v1.hide(); v2.hide(); ... v10.hide(); v11.hide(); }
void show() { v1.show(); v2.show(); ... v10.show(); v11.show(); }
};
All these functions are implemented in every component class,
according to the expected 'behavior'.
e.g.
class X_Behavior {
public:
void clear();
void hide();
void show();
...
};
Manual copying if these iterations
void clear() { v1.clear(); v2.clear(); ... v10.clear(); v11.clear(); }
void hide() { v1.hide(); v2.hide(); ... v10.hide(); v11.hide(); }
void show() { v1.show(); v2.show(); ... v10.show(); v11.show(); }
... more similar members here ...
is hard to maintain and review.
There are many classes like MyObject, each with many member variables.
Many developers edit them.
Also, you cannot tell whether an ommitted call or a mixed order was intentional or not.
Can you propose a compiler-generated construct that allows me to implement these functions once and not touch them again?
void MyObject::clear() { /* call clear() for every (_Behavior) member of this class */ }
void MyObject::hide() { /* call hide() for every (_Behavior) member of this class */ }
void MyObject::show() { /* call show() for every (_Behavior) member of this class */ }
I do not wish to increase the size of MyObject.
The *_Behavior classes should stay as they are.
Not to be tied to a base class.
I want to do this without employing the Preprocessor.
Can you propose a C++11/17/20 solution for this?
Ideally, I would like to see if this could be done with minimal code, just like
the compiler generated default implementations for constructor, copy constructor, assignments, destructor.
1. std::tuple + std::apply
A simple C++17 solution to your problem would be to add an additional method that returns references to all behaviors, then you can use std::apply with a templated lambda to reduce it to the individual calls.
e.g.: godbolt example
class MyObject {
BehaviorA v1;
BehaviorA v2;
BehaviorB v3;
BehaviorB v4;
constexpr auto behaviors() { return std::tie(v1, v2, v3, v4); }
public:
void clear() {
std::apply(
[](auto&&... behavior) { (behavior.clear(), ...); },
behaviors()
);
}
};
Pros:
Easily optimizeable by compilers, will mostly result in the same code as the manual function calls
Cons:
You have to remember to add each new behavior to behaviors().
2. boost::pfr::for_each_field
If you don't mind using boost you can enhance this by putting all the behaviors into an aggregate struct (since C++14 (but C++17 makes it a lot easier) you can sort-of reflect the members of aggregates by using aggregate initialization - this is often called "magic tuple")
e.g.: godbolt example
struct BehaviorA { void clear() { std::cout << "CLEAR A" << std::endl; } };
struct BehaviorB { void clear() { std::cout << "CLEAR B" << std::endl; } };
class MyObject {
struct MyObjectBehaviours {
BehaviorA v1;
BehaviorA v2;
BehaviorB v3;
BehaviorB v4;
} behaviors;
public:
void clear() {
boost::pfr::for_each_field(behaviors, [](auto&& behavior) {
behavior.clear();
});
}
};
Pros:
Very hard to mess up with this one
Can be optimized very good
Cons:
Needs boost
2.1 magic tuples without boost
You can also do the same without using boost, you'll have to write quite a bit of code though:
godbolt example
template<class T>
concept aggregate = std::is_aggregate_v<T>;
struct any_type {
template<class T>
operator T() {}
};
template<aggregate T>
consteval std::size_t count_members(auto ...members) {
if constexpr (requires { T{ members... }; } == false)
return sizeof...(members) - 1;
else
return count_members<T>(members..., any_type{});
}
template<aggregate T>
constexpr auto tie_struct(T& data) {
constexpr std::size_t fieldCount = count_members<T>();
if constexpr(fieldCount == 0) {
return std::tie();
} else if constexpr (fieldCount == 1) {
auto& [m1] = data;
return std::tie(m1);
} else if constexpr (fieldCount == 2) {
auto& [m1, m2] = data;
return std::tie(m1, m2);
} else if constexpr (fieldCount == 3) {
auto& [m1, m2, m3] = data;
return std::tie(m1, m2, m3);
} else if constexpr (fieldCount == 4) {
auto& [m1, m2, m3, m4] = data;
return std::tie(m1, m2, m3, m4);
} else {
static_assert(fieldCount!=fieldCount, "Too many fields for tie_struct! add more if statements!");
}
}
template<aggregate T, class Callable>
constexpr void for_each_field(T& data, Callable&& callable) {
std::apply([&callable](auto&&... members){
(callable(members), ...);
}, tie_struct(data));
}
struct BehaviorA { void clear() { std::cout << "CLEAR A" << std::endl; } };
struct BehaviorB { void clear() { std::cout << "CLEAR B" << std::endl; } };
class MyObject {
struct MyObjectBehaviours {
BehaviorA v1;
BehaviorA v2;
BehaviorB v3;
BehaviorB v4;
} behaviors;
public:
void clear() {
for_each_field(behaviors, [](auto&& behavior) {
behavior.clear();
});
}
};
Pros:
Same as above
Cons:
Needs a lot of boilerplate code (but that can also be used for other things - structure reflection is always useful :D )
3. std::variant array
With std::variants you can combine all your behaviors into a single array (it's basically an union of all possible behaviors), then you can use a simple for-loop with std::visit to access the individual behaviors:
e.g.: godbolt example
struct BehaviorA { BehaviorA(int) {} void clear() { std::cout << "CLEAR A" << std::endl; } };
struct BehaviorB { BehaviorB(float) {} void clear() { std::cout << "CLEAR B" << std::endl; } };
class MyObject {
using Behavior = std::variant<BehaviorA, BehaviorB>;
Behavior behaviors[4];
public:
MyObject() : behaviors {
Behavior{std::in_place_type<BehaviorA>, 1},
Behavior{std::in_place_type<BehaviorA>, 2},
Behavior{std::in_place_type<BehaviorB>, 1.0f},
Behavior{std::in_place_type<BehaviorB>, 2.0f}
} {
}
void clear() {
for(auto& b : behaviors)
std::visit([](auto& behavior) {
behavior.clear();
}, b);
}
};
Pros:
Easy to use, no allocations
Cons:
If you want to access only a single element it gets hairy, e.g.:
auto& b = std::get<BehaviorA>(behaviors[0]);
No names for the individual behaviors, only array indices
Potentially wastes a lot of memory (if some behaviors are a lot larger than others)
I think that your best option is to create a base class for the Behavior classes. If you really want to avoid that, you could store them as unions (or std::variants, but it would make the code needlessly more complicated and less readable.
Related
I'd like to implement access to a certain class:
class A { some properties and methods };
The problem is there are multiple states A can be in and the methods need to behave accordingly. One way is this:
class A
{
void Method1() {
if (A is in state 1) { do something }
else if (A is in state 2) { do something else }
...
}
};
That obviously isn't very optimal, if the methods are called many times. So a solution, which is simple to implement, would be to create several classes for different states:
class A
{
class State1 {
virtual void Method1(A& a) { do something; }
...
} State1Instance;
class State2 { ... }
...
};
And then manage a pointer to the object depending on current state (e.g. State1Instance) and call methods of this object. That avoids the CPU consuming condition.
BUT the State# methods also receive the completely useless "this" pointer to the State object. Is there a way to avoid this? I know the difference is minimal, but I'm trying to make this as optimal as possible and using a CPU register for a completely pointless value is not ideal. This would actually be a good use for "virtual static", which is forbidden however.
Just use good old function pointers if you're really concerned about the repeated branches, which usually you shouldn't.
struct A
{
using StateFn = void (*)(A&);
static void State1(A& a) { a.i = 42; }
static void State2(A& a) { a.i = 420; }
void Method1() { s(*this); }
StateFn s = State1;
int i;
};
If you have multiple methods associated with each state, a table of methods can be constructed as such
struct A
{
static void State1M1(A& a) { a.i = 42; }
static void State2M1(A& a) { a.i = 420; }
static int State1M2(A& a) { return a.i * 42; }
static int State2M2(A& a) { return a.i * 420; }
// The naming sucks, you should find something better
static constexpr struct {
void (*Method1)(A&);
int (*Method2)(A&);
} State[] = {{State1M1, State1M2}, {State2M1, State2M2}};
void Method1() { State[s].Method1(*this); }
int Method2() { return State[s].Method2(*this); }
int s, i;
};
I'm curious if this is even a speedup over a switch statement, do benchmark before you adopt it. You really aren't doing something too different from polymorphism, in a rather un-optimized manner, when you start constructing a method table like in the second case.
If you really want to go with this, use free or static functions, not polymorphy, and encapsulate them with ::std::function. You can even use lambdas, here.
class A {
public:
::std::function<void(A*)> state = func1;
static void func1(A* that) {
::std::cout << "func1\n";
that->state = func2;
}
static void func2(A* that) {
::std::cout << "func2\n";
that->state = [](A* that) { ::std::cout << "lambda\n"; that->state = func1; };
}
public:
void method() {
state(this);
}
};
However, in most cases a switch or else if block would be better as it can be optimised by the compiler, which may translate it into a jump table. If in doubt, benchmark it!
One of the most versatile solutions available out of the box in c++17, and courtesy of boost prior is the variant type and the concept of a static_visitor.
Using c++14 and boost::variant I have created a very simple state machine that uses type-based switching of code paths plus automatic catching of un-accounted-for state/event combinations.
For a fuller solution I would refer you to the boost fsm header-only library.
#include <boost/variant.hpp>
#include <iostream>
#include <typeinfo>
struct event1 {
};
struct event2 {
};
struct state_machine {
struct state1 {
};
struct state2 {
};
struct state3 {
};
using state_type = boost::variant<state1, state2, state3>;
struct handle_event {
// default case for event/state combinations we have not coded for
template<class SM, class State, class Event>
void operator()(SM &sm, State &state, Event const&event) const {
std::cout << "unhandled event "
"state=" << typeid(state).name() << " "
"event=" << typeid(event).name() << std::endl;
}
template<class SM>
void operator()(SM &sm, state1 &state, event1 const&event) const {
std::cout << "received event1 while in state1 - switching to state 2" << std::endl;
sm.change_state(state2());
}
template<class SM>
void operator()(SM &sm, state2 &state, event2 const&event) const {
std::cout << "received event2 while in state2 - switching to state 1" << std::endl;
sm.change_state(state1());
}
template<class SM>
void operator()(SM &sm, state1 &state, event2 const&event) const {
std::cout << "received event2 while in state1 - switching to state 3" << std::endl;
sm.change_state(state3());
}
};
template<class Event>
auto notify_event(Event const&evt) {
return boost::apply_visitor([this, &evt](auto& state)
{
handle_event()(*this, state, evt);
}, state_);
}
template<class NewState>
void change_state(NewState&& ns) {
state_ = std::forward<NewState>(ns);
}
private:
state_type state_ = state1{};
};
int main()
{
state_machine sm {};
sm.notify_event(event1());
sm.notify_event(event2());
sm.notify_event(event2());
// we have not coded for this one
sm.notify_event(event2());
}
example output (exact output will depend on compiler ABI):
received event1 while in state1 - switching to state 2
received event2 while in state2 - switching to state 1
received event2 while in state1 - switching to state 3
unhandled event state=N13state_machine6state3E event=6event2
I will begin with an example. Suppose I need to guard a code with a function inside a mutex. There are two ways of implementing this.
#include <iostream>
#include <vector>
#include <pthread.h>
pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;
std::vector<float> myVec;
void threadfunc(int i, float value)
{
pthread_mutex_lock(&myMutex);
if(i <= 0 || i > myVec.size())
{
pthread_mutex_unlock(&myMutex);
return;
}
if(value < 0)
{
pthread_mutex_unlock(&myMutex);
return;
}
myVec[i] += value;
pthread_mutex_unlock(&myMutex);
return;
}
class AUTOMUTEX
{
private:
pthread_mutex_t *mMutex;
public:
AUTOMUTEX(pthread_mutex_t *mutex): mMutex(mutex)
{
pthread_mutex_lock(mMutex);
}
~AUTOMUTEX()
{
pthread_mutex_unlock(mMutex);
}
};
void threadfunc_autolock(int i, float value)
{
AUTOMUTEX autoMutex(&myMutex);
if(i <= 0 || i > myVec.size())
{
return;
}
if(value < 0)
{
return;
}
myVec[i] += value;
return;
}
int main()
{
threadfunc_autolock(5, 10);
threadfunc(0, 7);
return 1;
}
As it is clear from the example threadfunc autolock is better implementation as calling pthread_mutex_unlock function return is taken care by destructor call to AUTOMUTEX (C++ 11 thread has support for this. So we don't need our own implementation of AUTOMUTEX if we are using C++11 thread library).
Is there a way we can achieve this without implementing a wrapper class each time we need to do this with some set/reset function pair. Does boost or C++ 11 have some predefined template class with which we can achieve the behaviour of AUTOMUTEX for any such "set/reset" sort of function. This is really helpful for functions with multiple points of return.
In other words does boost/C++ provide a class with the following behaviour.
//sample code not compilable.
template <class T, class Y>
class myAuto
{
myAuto()
{
T();
}
~myAuto()
{
Y();
};
You may write your own geneirc RAII class, something like:
class Finally
{
public:
explicit Finally(std::function<void()> f) : mF(f) {}
~Finally() noexcept() {
try
{
mF();
} catch (...) {
// Handle error.
}
}
Finally(const Finally&) = delete;
Finally(Finally&&) = delete;
Finally& operator=(const Finally&) = delete;
Finally& operator=(Finally&&) = delete;
private:
std::function<void()> mF;
};
Usage:
{
pthread_mutex_lock(&myMutex);
Finally finally([&](){ pthread_mutex_unlock(&myMutex); });
//..
}
Even if a dedicated RAII object may be more appropriate in some case (as Mutex).
There is a proposal for a generic scope guard to be included in the next C++ standard, and I think it is accepted. You can find an implementation here, together with a link to the reference paper.
In principle, it is similar to the classical ScopeGuard, but it also provides some special cases e.g. for C-like file APIs.
You could use something like ScopeGuard. (Now somewhat old-fashioned.)
But given how easy and clear it is to construct a specific RAII wrapper for each resource type I would normally do that.
(I don't think boost ever adopted anything like ScopeGuard. With std::function, lambdas and so on it's easy to do your own.)
What's wrong with writing your own generic resource wrapper?
template <typename Res, typename Fn = std::function<void(Res*)>>
class resource_mgr
{
Res* resource;
Fn initialize, finalize;
public:
resource_mgr (Res* r, Fn i, Fn f)
: resource(r),
initialize(i),
finalize(f)
{
initialize(resource);
}
resource_mgr (resource_mgr const&) = delete;
resource_mgr (resource_mgr&&) = delete;
resource_mgr const& operator = (resource_mgr const&) = delete;
resource_mgr const& operator = (resource_mgr&&) = delete;
~resource_mgr
{
try
{
finalize(resource);
}
catch(...)
{
std::cerr << "Uh-oh!"
}
}
};
You can keep it simple or go wild on something like this -- use smart pointers, define move operations, add support for custom error handlers, etc. You might use it like this:
void threadfunc_autolock(int i, float value)
{
resource_mgr<mutex_t> autoMutex (
&myMutex,
[](auto* p) { if (!pthread_mutex_lock(p)) throw Something(); },
[](auto* p) { if (!pthread_mutex_unlock(p)) throw Something(); }
);
/* . . . */
}
Here's an example using Boost.ScopeExit (untested):
#include <boost/scope_exit.hpp>
...
void threadfunc_autolock(int i, float value)
{
pthread_mutex_lock(&myMutex);
BOOST_SCOPE_EXIT(&myMutex) {
pthread_mutex_unlock(&myMutex);
} BOOST_SCOPE_EXIT_END
if(i <= 0 || i > myVec.size())
{
return;
}
if(value < 0)
{
return;
}
myVec[i] += value;
}
I am wanting to make a class which allows me to lock an object from being modified. It would essentially be a template with a boolean specifying the lock state. Since it is a template, I won't know all the methods that can be called on the internal object, so I need a method to pass calls through...
template<class T>
class const_lock
{
public:
const_lock() : my_lock(false) {}
void set_const_lock(bool state) {my_lock = state;}
// HOW TO IMPLEMENT SOMETHING LIKE THESE????
//
template<typename...Args >
auto operatorANY_OPERATOR (Args...args)
{
if(my_lock != false)
throw std::exception("Objected locked to modification");
return my_value.ANY_OPERATOR(args);
}
template<typename...Args >
auto operatorANY_CONST_OPERATOR (Args...args) const
{
return my_value.ANY_CONST_OPERATOR(args);
}
template<typename...Args >
auto ANY_METHOD(Args...args)
{
if(my_lock != false)
throw std::exception("Objected locked to modification");
return my_value.ANY_METHOD(args);
}
template<typename...Args >
auto ANY_CONST_METHOD(Args...args) const
{
return my_value.ANY_CONST_METHOD(args);
}
private:
bool my_lock;
T my_value;
}
int main()
{
const_lock<std::vector<int>> v;
v.push_back(5);
v.push_back(7);
v.set_const_lock(true);
v.push_back(9); // fails compilation
std::cout << v.at(1) << std::endl; // ok
}
Any help would be appreciated. Thanks!
Edit: changed static assert to throw and exception
What you're trying to do looks rather difficult, but more importantly is over-complicated and unnecessary for what you're trying to do.
Essentially what you're trying to do (correct me if I'm wrong) is create a compile time check of whether you are supposed to able to modify an object at a given time. However, c++ already has a built in way of doing this. Simply declare or pass your object as const or const&, and the compiler will not allow you to modify non-mutable parts of the object. When you want to be able to modify it pass it without const. You can even cast it from const& to regular & when you want to go from code where you can't modify it directly to code where you can, though I don't recommend it.
edit: just saw a comment on the question about no reference arrays. Don't worry about that! The standard library has support for reference wrappers which allow you to essentially store references in arrays or anywhere else.
You can make a generic wrapper class that you can forward the function to using a lambda that captures a reference to the internal member. In this example I am just using an if statement to check if it is "locked" and if it is then we just modify a copy.
template<class T>
class const_lock
{
private:
bool my_lock;
mutable T my_value;
public:
const_lock() : my_lock(false) {}
void set_const_lock() { my_lock = true; }
template<typename F>
auto operator()(F f) const -> decltype(f(my_value))
{
if (my_lock)
{
T temp{my_value}; // make a copy
return f(temp);
}
else
return f(my_value); // modify wrraped value
}
};
int main()
{
const_lock<std::string> cl;
cl([](std::string& s) {
s = "foobar";
});
cl([](std::string& s) {
std::cout << s << std::endl;
});
cl.set_const_lock();
cl([](std::string& s) {
s = "we should still be foobar";
});
cl([](std::string& s) {
std::cout << s;
});
}
This is completely unimplementable. A trivial modification of your source code shows why this won't work.
int main()
{
const_lock<std::vector<int>> v;
v.push_back(5);
v.push_back(7);
if (rand() % 2)
v.set_const_lock(true);
v.push_back(9); // fails compilation
std::cout << v.at(1) << std::endl; // ok
}
You need to completely rethink your approach.
Below is an example illustrating what I would be trying to protect against
class Node
{
public:
Node(int id) : my_id(id) {}
// . . .
int id() {return my_id;}
private:
int my_id;
// . . .
};
class Grid
{
public:
Grid() {}
// . . .
void associate(Node* n) { my_nodes.push_back(n); }
private:
// . . .
std::vector<Node*> my_nodes;
};
Node* find(std::vector<Node>& Nodes, int ID)
{
for(auto i=Nodes.begin(); i!=Nodes.end(); ++i)
{
if (i->id() == ID)
{
return &*i;
}
}
}
main()
{
std::vector<Node> Nodes;
// fill Nodes with data
Grid Array;
Array.associate( find(Nodes,14325) );
Array.associate( find(Nodes,51384) );
Array.associate( find(Nodes,321684) );
// . . .
Nodes.push_back(Node(21616)); // this can invalidate my pointers in Array
}
If I was able to make my Nodes vairable be
const_lock<std::vector<Node>> Nodes;
then call
Nodes.set_const_lock(true);
after populating the data, I wouldn't need to worry about my pointers in Array getting messed up.
I'd like to make a command Mapper that accepts commands of a certain type and hands them over to runtime-registered members of various sub-classes of a common Bindable class.
As the sub-class members are of different types, I struggle with programming a working Mapper class. How do I need to implement it to make it work?
#include <iostream> // std::cout
#include <functional> // std::bind
#include <map> // std::map
#include <vector> // std::vector
struct Command {
int cmdNum;
int numArgs;
std::vector<int> args;
};
struct Invocation {
enum Source {
SOURCE_X = 0, SOURCE_Y, SOURCE_Z,
SOURCE_END
};
Source src;
Command cmd;
};
struct Bindable {
virtual void handleCmd(Command Cmd) = 0;
};
struct A : Bindable {
void handleCmd (Command cmd) {
std::cout << "called handler-method of class A" <<std::endl;
std::cout << "cmdNum: " << cmd.cmdNum <<std::endl;
}
};
struct B : Bindable {
void handleCmd (Command cmd) {
std::cout << "called handler-method of class B" <<std::endl;
std::cout << "cmdNum: " << cmd.cmdNum <<std::endl;
}
};
The problematic Mapper:
struct Mapper {
void bindCmd(Command cmd, Bindable* mBindable) {
//Fill a multimap with cmd.cmdNum as keys and mBindable as values
}
//Send cmd to each registered Bindable for the respective cmdNum
void handleInv(Invocation inv) {
auto mMatches = mBinds.equal_range(inv.cmd.cmdNum);
for(auto mMatch : mMatches) {
mMatch.second()->handleCmd(inv.cmd);
}
}
private:
std::multimap<int, Bindable*> mBinds;
};
The desired usage shall be:
int main() {
A a;
B b;
Command cmdA = {200, 4, {1,2,3,4}};
Command cmdB = {400, 3, {3,2,1}};
Command cmdC = {600, 2, {8,9}};
Invocation invA = {Invocation::SOURCE_X, cmdA};
Invocation invB = {Invocation::SOURCE_Z, cmdB};
Invocation invC = {Invocation::SOURCE_Z, cmdC};
Mapper mMapper;
//Register Commands
mMapper.bindCmd(cmdA, &a);
mMapper.bindCmd(cmdB, &a);
mMapper.bindCmd(cmdA, &b);
mMapper.bindCmd(cmdC, &b);
//React to incoming Invocations
mMapper.handleInv(invA); //Call handleCmd of a and b
mMapper.handleInv(invB); //Call handleCmd of a
mMapper.handleInv(invC); //Call handleCmd of b
}
The code in the OP works, as far as I can see, when two minor bugs are fixed:
std::multimap<int, Bindable*> mBinds;
void handleInv(Invocation inv) {
auto mMatches = mBinds.equal_range(inv.cmd.cmdNum);
for(auto mMatch : mMatches) { // 1
mMatch.second()->handleCmd(inv.cmd); // 2
}
}
1
std::multimap<K,V>::equal_range returns a std::pair of iterators, where the member first specifies the begin, and the member second the end of an iterator-range.
The range-based for loop expects on the right-hand side of the : something that can provide the begin and end of an iterator-range, but searches for free functions or member functions with the names begin and end. Therefore, we have to translate std::pair::first -> begin() and std::pair::second -> end().
There are of course library solutions for this (e.g. boost). A minimal solution could be:
template<typename It>
struct iterator_pair_range
{
It b;
It e;
It begin() const { return b; }
It end() const { return e; }
};
template<typename It>
auto make_iterator_pair_range(std::pair<It, It> const& p)
-> iterator_pair_range<It>
{ return {p.first, p.second}; }
for(auto mMatch : make_iterator_pair_range(mMatches)) {
2
mMatch.second()->handleCmd(inv.cmd); // 2
The member second of std::pair is a public data member, not a member function:
mMatch.second->handleCmd(inv.cmd); // 2
I'll suggest you post your code on CodeReview.SE, since there are more general, safer (e.g. lifetime issues) and possibly easier solutions to this general problem. For example, there is the boost.signals2 library; also there is the std::function wrapper that allows storing objects of arbitrary type, as long as they can be called with a certain signature.
-edit- i cant experiment ATM but will tonight. I am thinking maybe a typedef can be used to hold mut and can be used to declare a var. But my initial thought is typedefs don't play nice with templates so i'll have to check later tonight (for now, to class)
I was looking at this piece of code shown below and i was wondering how it might be possible to implement without using defines.
Since I cant compile the code (i don't have any mutex/multithreading libs currently installed) i'll just look at the code and think it out.
It seems like one can completely implement PROTECTED_WITH by inheriting a template class. The problem is now PROTECTED_MEMBER. It uses a name with ## to create a variable. This isnt much of a problem because we create a class which holds the variable with the () operator to make it appear as a function. However accessing is_held() the problem as i would like not to pass this or mut_ in.
My gut says with out of the box thinking its possible to solve this without defines and without passing in to each variable a this, function ptr or reference. I'll allow everyone to cheat and use c++0x features.
template<typename Mutex>
class TestableMutex {
public:
void lock() { m.lock(); id = this_thread::get_id(); }
void unlock() { id = 0; m.unlock(); }
bool try_lock() { bool b = m.try_lock();
if( b ) id = this_thread::get_id();
return b; }
bool is_held() { return id == this_thread::get_id(); }
private:
Mutex m;
atomic<thread::id> id;
// for recursive mutexes, add a count
};
#define PROTECTED_WITH(MutType) \
public: void lock() { mut_.lock(); } \
public: bool try_lock() { return mut_.try_lock(); } \
public: void unlock() { mut_.unlock(); } \
private: TestableMutex<MutType> mut_;
#define PROTECTED_MEMBER(Type,name) \
public: Type& name() { assert(mut_.is_held()); return name##_; } \
private: Type name##_;
struct MyData {
PROTECTED_WITH( some_mutex_type );
PROTECTED_MEMBER( vector<int>, v );
PROTECTED_MEMBER( Widget*, w );
};
You can use an explicit specialization containing using declarations to list the objects protected by the mutex. Then use a base class to "pass" the access out to the user via operator->, so object->member (with object not being a pointer) performs the mutex assertion.
This is easier done than said:
// Imagine that the members of this class must be locked by the mutex.
class a : public expose_locked_by_arrow< a > {
protected:
int i;
void f();
};
// Declare which members are conditionally locked. Pretty simple and idiomatic.
template<>
struct member_expose< a > : a {
using a::i;
using a::f;
};
#include <iostream>
// Access mutex-locked members with ->
int main() {
a x;
x->i = 5;
a const y( x );
std::cout << y->i << '\n';
}
The library code:
// This template is specialized for each mutex protection client.
template< class >
struct member_expose;
// Base class provides mutex; parameter is derived class (CRTP).
template< class c >
struct expose_locked_by_arrow {
member_expose< c > *
operator->() {
assert ( expose_lock_mutex.is_held() );
return static_cast< member_expose< c > * >( this );
}
member_expose< c > const *
operator->() const {
assert ( expose_lock_mutex.is_held() );
return static_cast< member_expose< c > const * >( this );
}
expose_locked_by_arrow( mutex const &m = mutex() )
: expose_lock_mutex( m ) {}
protected:
mutex expose_lock_mutex;
};
See it run.
The #defines aren't providing any protection as such, rather they are just reducing the amount of typing you'd have to do (in turn, they make sure all the "protected" members have the proper code in place).
There isn't a way that I am aware of to avoid having to put the checks into each getter function - and locking the whole object, as they are returning references to data stored within the protected object.
If however, they could all be returned by value (or not returning anything at all), then you could use a container that locks everything using a proxy object, something like the following (this could probably be done better, i've just quickly hacked it together):
#include <iostream>
struct Mutex
{
void lock()
{
std::cout << "Mutex::lock" << std::endl;
}
void unlock()
{
std::cout << "Mutex::unlock" << std::endl;
}
};
template <class Object>
class ThreadSafeObject
{
mutable Mutex d_mutex;
Object d_object;
public:
struct Proxy
{
mutable Mutex *d_mutex;
Object *d_object;
Proxy(Mutex *mutex, Object *object)
: d_mutex(mutex)
, d_object(object)
{
d_mutex->lock();
}
Proxy(const Proxy& proxy)
: d_mutex(proxy.d_mutex)
, d_object(proxy.d_object)
{
proxy.d_mutex = NULL;
}
~Proxy()
{
if (d_mutex)
{
d_mutex->unlock();
}
}
Object *operator->()
{
return d_object;
}
};
struct ConstProxy
{
mutable Mutex *d_mutex;
const Object *d_object;
ConstProxy(Mutex *mutex, const Object *object)
: d_mutex(mutex)
, d_object(object)
{
d_mutex->lock();
}
ConstProxy(const ConstProxy& proxy)
: d_mutex(proxy.d_mutex)
, d_object(proxy.d_object)
{
proxy.d_mutex = NULL;
}
~ConstProxy()
{
if (d_mutex)
{
d_mutex->unlock();
}
}
const Object *operator->() const
{
return d_object;
}
};
Proxy operator->()
{
return Proxy(&d_mutex, &d_object);
}
ConstProxy operator->() const
{
return ConstProxy(&d_mutex, &d_object);
}
};
struct Foo
{
void foo()
{
std::cout << "Foo::foo" << std::endl;
}
};
int main()
{
ThreadSafeObject<Foo> myFoo;
myFoo->foo();
return 0;
}
Which uses the operator->() trick (when operator-> doesnt reutrn a pointer type, the compiler will keep calling operator-> on the returned values until eventually a regular pointer type is returned) and gives the following output:
Mutex::lock
Foo::foo
Mutex::unlock
Generally speaking though, an object that needs to be used by multiple threads shouldn't be exposing its internals like that, it would be safer to have it accept parameters and use its internal values to act on them.