Extend object lifetime/scope from a `if constexpr` branch - c++

Say we have the following code
struct MyClass
{
MyClass() = delete; // or MyClass() { }
MyClass(int) { }
void func() { }
};
int main()
{
if constexpr (std::is_default_constructible_v<MyClass>) {
MyClass myObj;
} else {
MyClass myObj(10);
}
myObj.func(); // Error
}
Here I am using if constexpr to determine whether the class is default-constructible (or not), and then create an object accordingly. In a way, I naively thought this would simplify the different branches down to just the one that's true, i.e.
if constexpr (true) {
/* instruction branch 1 */
} else if constexpr (false) {
/* instruction branch 2 */
}
simply becomes
/* instruction branch 1 */
But in reality, it is probably more like this
{
/* instruction branch 1 */
}
But then the question becomes (going to back to the the very first example), how can I can I keep myObj in scope outside the { ... }?

You can't extend the lifetime of an object with automatic storage duration beyond the scope in which it's created.
What you can do is create uninitialized storage outside your if block and create an object in that storage within the scope of the if. The easiest way to do that is probably std::optional:
template <typename T>
void foo() {
std::optional<T> obj;
if constexpr (std::is_default_constructible_v<T>) {
obj.emplace();
} else {
obj.emplace(10);
}
obj->func();
}
Live Demo
This does result in a small amount of overhead though, since std::optional has to hold an extra flag to determine if it holds an object or not. If you want to avoid that overhead you could manage the storage yourself:
template <typename T>
void foo() {
std::aligned_storage_t<sizeof(T), alignof(T)> storage;
T* ptr;
if constexpr (std::is_default_constructible_v<T>) {
ptr = new(&storage) T{};
} else {
ptr = new(&storage) T{10};
}
struct destroy {
destroy(T* ptr) : ptr_{ptr} {}
~destroy() { ptr_->~T(); }
T* ptr_;
} destroy{ptr};
ptr->func();
}
Live Demo
Note that in both cases I've moved the functionality to a function template. For if constexpr to discard a branch it must be dependent on a template parameter. If you try to do this directly in main the false branch will not be discarded and you will get an error complaining about a missing default constructor.

First, your code won't work. if constexpr really needs its condition to be dependent.
I'll fix it.
template<class MyClass>
void func() {
MyClass myObj = []{
if constexpr (std::is_default_constructible_v<MyClass>) {
return MyClass{};
} else {
return MyClass(10);
}
}();
myObj.func();
}
now
int main() {
func<MyClass>();
}
solves your problem.
Note that under c++17 rules, no copies or moves of MyClass occur in the above code.

Related

Is it possible to implement a DefaultIfNull function in C++?

Disclaimer: This is rather more out of curiosity than for a lack of other solutions!
Is it possible to implement a function in C++ that:
gets passed a pointer of type T
either returns a reference-like-thing to the object pointed to by T
or, if the pointer is null, returns a reference-like-thing to a default constructed T() that has some sane lifetime?
Our first try was:
template<typename T>
T& DefaultIfNullDangling(T* ptr) {
if (!ptr) {
return T(); // xxx warning C4172: returning address of local variable or temporary
} else {
return *ptr;
}
}
A second attempt was done like this:
template<typename T>
T& DefaultIfNull(T* ptr, T&& callSiteTemp = T()) {
if (!ptr) {
return callSiteTemp;
} else {
return *ptr;
}
}
This gets rid of the warning and somewhat extends the lifetime of the temporary, but it's still rather error prone, I think.
Background:
The whole thing was triggered by an access pattern that looked like this:
if (pThing) {
for (auto& subThing : pThing->subs1) {
// ...
if (subThing.pSubSub) {
for (auto& subSubThing : *(subThing.pSubSub)) {
// ...
}
}
}
}
that could be "simplified" to:
for (auto& subThing : DefaultIfNull(pThing).subs1) {
// ...
for (auto& subSubThing : DefaultIfNull(subThing.pSubSub)) {
// ...
}
}
Yes, but it's going to be ugly:
#include <stdio.h>
#include <variant>
template <class T>
struct Proxy {
private:
std::variant<T*, T> m_data = nullptr;
public:
Proxy(T* p) {
if (p)
m_data = p;
else
m_data = T{};
}
T* operator->() {
struct Visitor {
T* operator()(T* t) { return t; }
T* operator()(T& t) { return &t; }
};
return std::visit(Visitor{}, m_data);
}
};
struct Thing1 {
int pSubSub[3] = {};
auto begin() const { return pSubSub; }
auto end() const { return pSubSub + 3; }
};
struct Thing2 {
Thing1* subs1[3] = {};
auto begin() const { return subs1; }
auto end() const { return subs1 + 3; }
};
template <class T>
auto NullOrDefault(T* p) {
return Proxy<T>(p);
}
int main() {
Thing1 a{1, 2, 3}, b{4, 5, 6};
Thing2 c{&a, nullptr, &b};
auto pThing = &c;
for (auto& subThing : NullOrDefault(pThing)->subs1) {
for (auto& subSubThing : NullOrDefault(subThing)->pSubSub) {
printf("%d, ", subSubThing);
}
putchar('\n');
}
}
There isn't really a good, idiomatic C++ solution that would exactly match what you're asking for.
A language where "EmptyIfNull" would work well, is probably one that has either garbage collection, or reference counted objects. So, we can achieve something similar in C++ by using reference counted pointers:
// never returns null, even if argument was null
std::shared_pr<T>
EmptyIfNull(std::shared_pr<T> ptr) {
return ptr
? ptr
: std::make_shared<T>();
}
Alternatively, you could return a reference to an object with static storage duration. However, I would not return a mutable reference when using such technique, since one caller might modify the object to be non-empty which might be highly confusing to another caller:
const T&
EmptyIfNull(T* ptr) {
static T empty{};
return ptr
? *ptr
: empty;
}
Alternatively, you could still return a mutable reference, but document that not modifying the empty object is a requirement that the caller must obey. That would be brittle, but that's par for the course in C++.
As another alternative, I was writing a suggestion to use a type-erasing wrapper that is either a reference, or an object, but Ayxan Haqverdili has got it covered already. Tons of boilerplate though.
Some alternative designs that adjust the premise a bit more, to be suitable to C++:
Return an object:
T
EmptyIfNull(T* ptr) {
return ptr
? *ptr
: T{};
}
Let the caller provide the default:
T&
ValueOrDefault(T* ptr, T& default_) {
return ptr
? *ptr
: default_;
}
Treat a non-null argument as a pre-condition:
T&
JustIndirectThrough(T* ptr) {
assert(ptr); // note that there may be better alternatives to the standard assert
return *ptr;
}
Treat a null argument as an error case:
T&
JustIndirectThrough(T* ptr) {
if (!ptr) {
// note that there are alternative error handling mechanisms
throw std::invalid_argument(
"I can't deal with this :(");
}
return *ptr;
}
Background:
I don't think the function that you're asking for is very attractive for the background that you give. Currently, you do nothing if the pointer is null, while with this suggestion you would be doing something with an empty object. If you dislike the deeply nested block, you could use this alternative:
if (!pThing)
continue; // or return, depending on context
for (auto& subThing : pThing->subs1) {
if (!subThing.pSubSub)
continue;
for (auto& subSubThing : *subThing.pSubSub) {
// ...
}
}
Or, perhaps you could establish an invariant that you never store null in the range, in which case you never need to check for null.
Sadly, but no. There is really no way to fully achieve what you want. Your options are:
If passed pointer is nullptr, return a reference to static object. This would only be correct if you are returning a const reference, otherwise, you are exposing yourself to a huge can of worms;
Return an std::optional<std::ref> and return unset optional if pointer is nullptr. This doesn't really solve your problem, as you still have to check at the call site if the optional is set, and you might as well check for the pointer to be nullptr instead at the call site. Alternatively, you can use value_or to extract value from optional, which would be akin to next option in a different packaging;
Use your second attempt, but remove default argument. This will mandate call site to provide a default object - this makes code somewhat ugly
If you only want to skip over nullptrs easily, you could just use boost::filter_iterator.
Now, this does not return default value on null pointer occurence, but neither does OP's original code; instead it wraps the container and provides the API to silently skip it in the for loop.
I skipped all the boilerplate code for brevity, hopefully the snippet below illustrates the idea well.
#include <iostream>
#include <memory>
#include <vector>
#include <boost/iterator/filter_iterator.hpp>
struct NonNull
{
bool operator()(const auto& x) const { return x!=nullptr;}
};
class NonNullVectorOfVectorsRef
{
public:
NonNullVectorOfVectorsRef(std::vector<std::unique_ptr<std::vector<int>>>& target)
: mUnderlying(target)
{}
auto end() const
{
return boost::make_filter_iterator<NonNull>(NonNull(), mUnderlying.end(), mUnderlying.end());
}
auto begin() const
{
return boost::make_filter_iterator<NonNull>(NonNull(), mUnderlying.begin(), mUnderlying.end());
}
private:
std::vector<std::unique_ptr<std::vector<int>>>& mUnderlying;
};
int main(int, char*[])
{
auto vouter=std::vector<std::unique_ptr<std::vector<int>>> {};
vouter.push_back(std::make_unique<std::vector<int>>(std::vector<int>{1,2,3,4,5}));
vouter.push_back(nullptr);
vouter.push_back(std::make_unique<std::vector<int>>(std::vector<int>{42}));
auto nn = NonNullVectorOfVectorsRef(vouter);
for (auto&& i:nn) {
for (auto&& j:(*i)) std::cout << j << ' ';
std::cout << '\n';
}
return 0;
}
If you accept std::shared_ptr<T>, you could use them to achieve this in a rather save and portable way:
template<typename T>
std::shared_ptr<T> NullOrDefault(std::shared_ptr<T> value)
{
if(value != nullptr)
{
return value;
}
return std::make_shared<T>();
}
From the comments:
One solution would be to implement a proxy range type containing a
pointer. This type would provide the begin and end members which
either forward the call to the pointed container or provide an empty
range. The usage would be basically identical to using a NullOrEmpty
function, in the context of a range-based for loop. – François
Andrieux yesterday
This is basically similar to what Ayxan provided in another answer, though this one here does work with exactly the client side syntax shown in the OP by providing begin() and end():
template<typename T>
struct CollectionProxy {
T* ref_;
// Note if T is a const-type you need to remove the const for the optional, otherwise it can't be reinitialized:
std::optional<typename std::remove_const<T>::type> defObj;
explicit CollectionProxy(T* ptr)
: ref_(ptr)
{
if (!ref_) {
defObj = T();
ref_ = &defObj.value();
}
}
using beginT = decltype(ref_->begin());
using endT = decltype(ref_->end());
beginT begin() const {
return ref_->begin();
}
endT end() const {
return ref_->end();
}
};
template<typename T>
CollectionProxy<T> DefaultIfNull(T* ptr) {
return CollectionProxy<T>(ptr);
}
void fun(const std::vector<int>* vecPtr) {
for (auto elem : DefaultIfNull(vecPtr)) {
std::cout << elem;
}
}
Notes:
Allowing for T and T const seems a wee bit tricky.
The solution using a variant would generate a smaller proxy object size (I think).
This is certainly gonna be more expensive at runtime than the if+for in the OP, after all you have to at least construct an (empty) temporary
I think providing an empty range could be done cheaper here if all you need is begin() and end(), but if this should generalize to more than just calls to begin() and end(), you would need a real temporary object of T anyways.

How are template arguments expanded

I am confused about the expansion of this template [example one]. If bool b is checked at runtime in the constructor where is b stored ? is it put into the private data section [example two]? or does it become compile time and remove a branched based on the bool? or does it simply "paste" what was passed as a template argument into the if(b) [example 3]
Example one:
template<bool b>
class Test
{
public:
Test()
{
if(b)
{
// do something
}
else
{
// do something else
}
}
};
Example two:
class Test
{
public:
Test()
{
if(b)
{
// do something
}
else
{
// do something else
}
}
private:
bool b = true;
};
Example three:
//called with Test<true>
class Test
{
public:
Test()
{
if(true)
{
// do something
}
else
{
// do something else - probably removed due too compiler optimization
}
}
};
Example 3 is the snippet that more closely resembles what the compiler is doing. It's important to understand that example 2 is wrong, as the template parameter is evaluated at compile-time and not injected into the class as a field.
Doing if(b){ } else { } where b is a template bool parameter will require both branches of the if statement to be both parseable and well-formed, even if the compiler will very likely optimize out the branch that doesn't match b.
If you want guaranteed compile-time branch evaluation, and if you need only the taken branch to be well-formed, you can use if constexpr(...) in C++17:
if constexpr(b)
{
// do something
}
else
{
// do something else
}
...or implement your own static_if construct in C++14...
...or use an explicit template specialization in C++11.
I cover all of these techniques in my CppCon 2016 talk, "Implementing static control flow in C++14".

Bypass a template error with a private destructor

In compile time, I've got the following issue, how to make this compile, because conceptually for me it's correct, any suggestions of refactoring are welcome.
I got a compile error because "Search" destructor is private but I won't use delete on a Search pointer since I provided a custom Deleter in the initialization of the base class. I know that the compiler doesn't know that, how to bypass it.
error description :
error C2248: cannot access private member declared in class 'Search'
compiler has generated 'Search::~Search' here
class Search
{
public:
static Search* New(/* */); // using a pool of already allocated objects to avoid expensive allocations
static void Delete(Search*);
private:
Search(/* */) {/* */}
~Search() {/* */}
};
template<class T>
class MyList
{
public:
typedef (*CustomDeleter) (T* pElement);
MyList(CustomDeleter lpfnDeleter = NULL) {};
void Empty()
{
for (/**/)
{
if (m_pList[m_nListLastUsed])
{
if (m_lpfnCustomDeleter == NULL)
delete m_pList[m_nListLastUsed]; // COMPILE ERROR HERE BECAUSE Search destructor is private BUT I won't use that instruction since
// I provided a custom Deletern I know that the compiler doesn't know that, how to bypass it
else
m_lpfnCustomDeleter(m_pList[m_nListLastUsed]);
}
}
}
private:
T** m_pList;
CustomDeleter m_lpfnCustomDeleter; // Pointer to a custom deleter
};
class Query : public MyList<Search>
{
public:
Query() : MyList<Search>(&Search::Delete) // I set a custom deleter since Search hides its destructor : is this the right way ?
{}
~Query()
{
/****/
Empty(); // PROBLEM HERE
/***/
}
};
Make sure that 'm_lpfnCustomDeleter' is never NULL or better nullptr. You can make sure of this by falling back to a default 'deleter' if the user does not provide with any custom deleter.
I would prefer something like below.
#include <iostream>
template <typename PointerType>
struct DefaultDeleter {
void operator()(PointerType* ptr) {
std::cout << "Delete\n";
}
};
struct CustomDeleter {
void operator()(int* ptr) {
std::cout << "Custom int deleter" << std::endl;
}
};
template <typename T, typename Deleter = DefaultDeleter<T>>
class Whatever
{
public:
Whatever() {
std::cout << "Cons\n";
}
void deinit() {
Deleter d;
auto v = new T;
d(v); // Just for the sake of example
}
};
int main() {
Whatever<char> w;
w.deinit();
Whatever<int, CustomDeleter> w2;
w2.deinit();
return 0;
}
Updated :: W/o code refactoring
Assuming w/o c++11
Have this small metaprogram added to your code base.
namespace my {
template <typename T, typename U> struct is_same {
static const bool value = false;
};
template <typename T>
struct is_same<T, T> {
static const bool value = true;
};
template <bool v, typename T = void> struct enable_if;
template <typename T = void> struct<true, T> {
typedef T type;
};
}
Change your Empty function to:
void Empty() {
for (/****/) {
do_delete();
}
}
template <typename =
typename my::enable_if<my::is_same<T, Search>::value>::type>
void do_delete() {
assert (m_lpfnCustomDeleter != NULL);
m_lpfnCustomDeleter(m_pList[m_nListLastUsed]);
}
void do_delete() {
delete m_pList[m_nListLastUsed];
}
If you are using c++11, the you dont have to write the metaprogram under namespace 'my'. Just replace 'my::is_same' and 'my::enable_if' with 'std::is_same' and 'std::enable_if'.
Note:, Have not compiled and tested the above code.
Separate the code doing the deleting from the rest:
if (m_pList[m_nListLastUsed])
{
if (m_lpfnCustomDeleter == NULL)
delete m_pList[m_nListLastUsed]; // COMPILE ERROR HERE BECAUSE Search destructor is private BUT I won't use that instruction since
// I provided a custom Deletern I know that the compiler doesn't know that, how to bypass it
else
m_lpfnCustomDeleter(m_pList[m_nListLastUsed]);
}
Replace the code above by a call to:
custom_delete(m_pList[m_nListLastUsed]);
Then add it as a method of your list class, don't forget to include <type_traits> as well:
std::enabled_if<std::is_destructible<T>::value, void>::type custom_delete(T* ptr) {
/* Note: this isn't pre-2000 anymore, 'lpfn' as a prefix is horrible,
don't use prefixes! */
if (m_lpfnCustomDeleter) {
m_lpfnCustomDeleter(ptr);
} else {
delete ptr;
}
}
std::enabled_if<!std::is_destructible<T>::value, void>::type custom_delete(T* ptr) {
if (!m_lpfnCustomDeleter) {
throw "No custom deleter for a non destructible type!";
}
m_lpfnCustomDeleter(ptr);
}
enabled_if will make it so that the function where it can delete the object directly doesn't exist in your list if the object has a private destructor.
Alternatively, you could pass a structure (or function) acting as a custom deleter as the second template argument of your list with a default value as one that calls the delete operator, then directly call this structure on your pointer, as in Arunmu's anser.

Return value optimization and destructor calls

I know that RVO is mostly applied but can I count on it? I have a function that creates an object of class FlagContainer.
class FlagContainer {
public:
~FlagContainer() {
someItem->flag = true;
}
private:
Item * someItem;
}
public FlagContainer createFlagContainer() {
return FlagContainer();
}
After the caller used the container, the flag must be set. So I can do this with the destructor.
{
FlagContainer container = createFlagContainer();
// do something with container
}
When out of scope, the destructor will be called. But can I be sure that the destructor will never be called in createFlagContainer? Is there any way to achieve this?
I would use AVR GCC 4.7.0 compiler.
I know that RVO is mostly applied but can I count on it?
Don't rely on RVO for logic. Put simply, someone compiling your program can switch it off with a command-line option.
Is there any way to achieve this?
Surprisingly, the standard library already gives you this functionality so you don't need to run the risk of implementing it yourself (move constructors and operators are notoriously difficult to get right)
std::unique_ptr with a custom deleter does the job nicely.
#include <iostream>
#include <memory>
#include <cassert>
// test type
struct X
{
bool flag = false;
};
// a custom deleter that sets a flag on the target
struct flag_setter_impl
{
template<class X>
void operator()(X* px) const {
if (px) {
assert(!px->flag);
std::cout << "setting flag!" << std::endl;
px->flag = true;
}
}
};
// a type of unique_ptr which does not delete, but sets a flag
template<class X>
using flag_setter = std::unique_ptr<X, flag_setter_impl>;
// make a flag_stter for x
template<class X>
auto make_flag_setter(X& x) -> flag_setter<X>
{
return flag_setter<X>(&x, flag_setter_impl());
}
// quick test
auto main() -> int
{
using namespace std;
X x;
{
auto fs1 = make_flag_setter(x);
auto fs2 = move(fs1);
}
return 0;
}
but I don't have the STL on my target
Then don't forget your rules of 0, 3, 5
#include <iostream>
#include <memory>
#include <cassert>
// test type
struct X
{
bool flag = false;
};
// a custom deleter that sets a flag on the target
struct flag_setter_impl
{
template<class X>
void operator()(X* px) const {
if (px) {
assert(!px->flag);
std::cout << "setting flag!" << std::endl;
px->flag = true;
}
}
};
// a type of unique_ptr which does not delete, but sets a flag
template<class X>
struct flag_setter
{
flag_setter(X* px) : px(px) {}
flag_setter(const flag_setter&) = delete;
flag_setter(flag_setter&& r) noexcept : px(r.px) { r.px = nullptr; }
flag_setter& operator=(const flag_setter& r) = delete;
flag_setter& operator=(flag_setter&& r)
{
flag_setter tmp(std::move(r));
std::swap(tmp.px, px);
return *this;
}
~flag_setter() noexcept {
flag_setter_impl()(px);
}
private:
X* px;
};
// make a flag_stter for x
template<class X>
auto make_flag_setter(X& x) -> flag_setter<X>
{
return flag_setter<X>(&x);
}
// quick test
auto main() -> int
{
using namespace std;
X x;
{
auto fs1 = make_flag_setter(x);
auto fs2 = move(fs1);
}
return 0;
}
There is no guarantee [yet] that copy-elision is applied. Guaranteed copy-elision is proposed for inclusion into C++17. Whether copy-elision is applied is entirely at the discretion of the compiler (some compilers have an option to entirely disable it, though).
A potential approach avoiding this need might be the use of an essentially unusable type which can be used only as the constructor argument for the type you are interested in being used and to return an object of that type:
class FlagContainerBuilder {
friend class FlagContainer;
public:
FlagContainerBuilder(/* suitable arguments go here */);
// nothing goes here
};
class FlagContainer {
// ...
public:
FlagContainer(FlagContainerBuilder&& builder);
// as before
};
FlagContainerBuilder createFlagContainer() { ... }
This way you avoid the need to potentially destroy a FlagContainer returned from createFlagContainer().
I know that RVO is mostly applied but can I count on it?
No. Compilers are allowed to implement RVO, but not required. You can only count on it, when your compiler promises to do so.
Although this particular case as per standard 12.8/3/p31.1 Copying and moving class objects [class.copy] renders as a context that the compiler can do NRVO (aka copy elision), you can't rely on it. A program that relies on this kind of optimization is effectively non portable.
To ensure move of the object I would define a move constructor and inside I would null the pointer of the other object, while in the destructor I would check whether the pointer is nullptr in order to set its flag true:
class FlagContainer {
public:
FlagContainer(FlagContainer&& other) : someItem(other.someItem) {
other.someItem = nullptr;
}
~FlagContainer() {
if(someItem) someItem->flag = true;
}
Item * someItem;
};
FlagContainer createFlagContainer() {
return FlagContainer();
}
Live Demo

Accessing template class structure type members

Ok, the question title is a bit hard to phrase. What I am trying to achieve is create a template class with get/set functions that can handle simple types and structures.
This is simple for types such as integers and char, etc... But when the template type 'T' is a struct then it gets harder.
For example, here is a template class, where I have omitted various parts of it (such as constructor, etc), but it shows the get/set function:
EDIT: Only this class is allowed to modify the data, so passing a reference outside is not allowed. The reason is that I want to do a mutex around the set/get. I will/have update the functions...
template <class T> class storage
{
private:
T m_var;
pthread_mutex_t m_mutex;
public:
void set(T value)
{
pthread_mutex_lock(&m_mutex);
m_var = value;
pthread_mutex_unlock(&m_mutex);
}
T get(void)
{
T tmp;
// Note: Can't return the value within the mutex otherwise we could get into a deadlock. So
// we have to first read the value into a temporary variable and then return that.
pthread_mutex_lock(&m_mutex);
tmp = m_var;
pthread_mutex_unlock(&m_mutex);
return tmp;
}
};
Then consider the following code:
struct shape_t
{
int numSides;
int x;
int y;
}
int main()
{
storage<int> intStore;
storage<shape_t> shapeStore;
// To set int value I can do:
intStore.set(2);
// To set shape_t value I can do:
shape_t tempShape;
tempShape.numSides = 2;
tempShape.x = 5;
tempShape.y = 4;
shapeStore.set(tempShape);
// To modify 'x' (and keep y and numSides the same) I have to do:
shape_t tempShape = shapeStore.get();
tempShape.x = 5;
shapeStore.set(tempShape);
}
What I want to be able to do, if its possible, is to set the members of shape_t individually via some means in the template class, something like:
shapeStore.set(T::numSides, 2);
shapeStore.set(T::x, 5);
shapeStore.set(T::y, 4);
And not have to use a temp var. Is this possible? how?
I looked at this answer, but it did not quite do what I wanted because it is for a specific structure type
Make your get() member return a reference:
T& get()
{
return m_var;
}
Then you could say
shapeStore.get().x = 42;
Note it is good practice to add a const overload:
const T& get() const
{
return m_var;
}
Also note that if your get and set methods really do nothing special, as in your example, you might consider making the data public and doing away with getters/setters:
template <class T> struct storage
{
T m_var;
};
Edit: If you want to allow synchronised changes to the member, an option is to have a method that takes a modifying function. The function is applied inside the class, in your case, protected by the mutex. For example,
template <class T> struct storage
{
storage() : m_var() {}
void do_stuff(std::function<void(T&)> f)
{
std::lock_guard<std::mutex> lock(m_mutex);
f(m_var);
}
private:
T m_var;
std::mutex_t m_mutex;
};
Then you can modify members in a synchronised manner:
storage<shape_t> shapeStore;
shapeStore.do_stuff([](shape_t& s)
{ s.x = 42;
s.y = 100; });
If you don't have C++11 you can pass a function instead:
void foo(shape_t& s) { s.x = 42; }
shapeStore.do_stuff(foo);
Your design is fairly workable for primitive types, but it requires you to replicate the entire interface of class types and quickly becomes unmanageable. Even in the case of primitive types, you might want to enable more complex atomic operations than simply get and set, e.g., increment or add or multiply. The key to simplifying the design is to realize that you don't actually want to interpose on every single operation the client code performs on the data object, you only need to interpose before and after the client code atomically performs a sequence of operations.
Anthony Williams wrote a great article in Doctor Dobb's Journal years ago about this exact problem using a design where the manager object provides a handle to the client code that the client uses to access the managed object. The manager interposes only on the handle creation and destruction allowing clients with a handle unfettered access to the managed object. (See the recent proposal for standardization for excruciating detail.)
You could apply the approach to your problem fairly easily. First, I'll replicate some parts of the C++11 threads library because they make it MUCH easier to write correct code in the presence of exceptions:
class mutex {
pthread_mutex_t m_mutex;
// Forbid copy/move
mutex(const mutex&); // C++11: = delete;
mutex& operator = (const mutex&); // C++11: = delete;
public:
mutex(pthread_mutex_) { pthread_mutex_init(&m_mutex, NULL); }
~mutex() { pthread_mutex_destroy(&m_mutex); }
void lock() { pthread_mutex_lock(&m_mutex); }
void unlock() { pthread_mutex_unlock(&m_mutex); }
bool try_lock() { return pthread_mutex_trylock(&m_mutex) == 0; }
};
class lock_guard {
mutex& mtx;
public:
lock_guard(mutex& mtx_) : mtx(mtx_) { mtx.lock(); }
~lock_guard() { mtx.unlock(); }
};
The class mutex wraps up a pthread_mutex_t concisely. It handles creation and destruction automatically, and saves our poor fingers some keystrokes. lock_guard is a handy RAII wrapper that automatically unlocks the mutex when it goes out of scope.
storage then becomes incredibly simple:
template <class> class handle;
template <class T> class storage
{
private:
T m_var;
mutex m_mutex;
public:
storage() : m_var() {}
storage(const T& var) : m_var(var) {}
friend class handle<T>;
};
It's simply a box with a T and a mutex inside. storage trusts the handle class to be friendly and allows it poke around its insides. It should be clear that storage does not directly provide any access to m_var, so the only way it could possibly be modified is via a handle.
handle is a bit more complex:
template <class T>
class handle {
T& m_data;
lock_guard m_lock;
public:
handle(storage<T>& s) : m_data(s.m_var), m_lock(s.m_mutex) {}
T& operator* () const {
return m_data;
}
T* operator -> () const {
return &m_data;
}
};
it keeps a reference to the data item and holds one of those handy automatic lock objects. The use of operator* and operator-> make handle objects behave like a pointer to T.
Since only way to access the object inside storage is through a handle, and a handle guarantees that the appropriate mutex is held during its lifetime, there's no way for client code to forget to lock the mutex, or to accidentally access the stored object without locking the mutex. It can't even forget to unlock the mutex, which is nice as well. Usage is simple (See it working live at Coliru):
storage<int> foo;
void example() {
{
handle<int> p(foo);
// We have exclusive access to the stored int.
*p = 42;
}
// other threads could access foo here.
{
handle<int> p(foo);
// We have exclusive access again.
*p *= 12;
// We can safely return with the mutex held,
// it will be unlocked for us in the handle destructor.
return ++*p;
}
}
You would code the program in the OP as:
struct shape_t
{
int numSides;
int x;
int y;
};
int main()
{
storage<int> intStore;
storage<shape_t> shapeStore;
// To set int value I can do:
*handle<int>(intStore) = 2;
{
// To set shape_t value I can do:
handle<shape_t> ptr(shapeStore);
ptr->numSides = 2;
ptr->x = 5;
ptr->y = 4;
}
// To modify 'x' (and keep y and numSides the same) I have to do:
handle<shape_t>(shapeStore)->x = 5;
}
I can propose you alternative solution.
When you need you can get special template class that allows managing containing object.
template < typename T >
class SafeContainer
{
public:
// Variadic template for constructor
template<typename ... ARGS>
SafeContainer(ARGS ...arguments)
: m_data(arguments ...)
{};
// RAII mutex
class Accessor
{
public:
// lock when created
Accessor(SafeContainer<T>* container)
:m_container(container)
{
m_container->m_mutex.lock();
}
// Unlock when destroyed
~Accessor()
{
m_container->m_mutex.unlock();
}
// Access methods
T* operator -> ()
{
return &m_container->m_data;
}
T& operator * ()
{
return m_container->data;
}
private:
SafeContainer<T> *m_container;
};
friend Accessor;
Accessor get()
{
return Accessor(this);
}
private:
T m_data;
// Should be using recursive mutex to avoid deadlocks
std::mutex m_mutex;
};
Example:
struct shape_t
{
int numSides;
int x;
int y;
};
int main()
{
SafeContainer<shape_t> shape;
auto shapeSafe = shape.get();
shapeSafe->numSides = 2;
shapeSafe->x = 2;
}