Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » initializing temporary from result of ...

Credits go to CGamesPlay, kazzmir, Kibiz0r, Kitty Cat, and Tobias Dammers for helping out!
This thread is locked; no one can reply to it. rss feed Print
 1   2 
initializing temporary from result of ...
Billybob
Member #3,136
January 2003

I'm creating a class template that will give me thread-safe primitives:

1#ifndef SAFE_VALUE_H_
2#define SAFE_VALUE_H_
3 
4#include <QReadWriteLock>
5 
6// Thread-safe access to a primative.
7template <typename T>
8class SafeValue
9{
10private:
11 T x;
12 QReadWriteLock lock;
13
14public:
15 SafeValue() : lock() {}
16 SafeValue(T y) : x(y), lock() {}
17 SafeValue(SafeValue<T> &other) : x(other), lock() {}
18
19 SafeValue &operator= ( const T &y ) {
20 lock.lockForWrite();
21 x = y;
22 lock.unlock();
23 return *this;
24 }
25
26 operator T() {
27 T y;
28 lock.lockForRead();
29 y = x;
30 lock.unlock();
31 return y;
32 }
33};
34 
35#endif /*SAFE_VALUE_H_*/

Whenever the value needs to be accessed a lock is put on it. So I can seamless access the value as if it were the original primitive, and yet it's completely thread-safe.

However, when I try to compile my project I can an error on the line that initializes a static member of my Manager class.

SafeValue<bool> Manager::executing = false;

manager.cpp:4: error: no matching function for call to ‘SafeValue<bool>::SafeValue(SafeValue<bool>)’
safe_value.h:17: note: candidates are: SafeValue<T>::SafeValue(SafeValue<T>&) [with T = bool]
safe_value.h:16: note:                 SafeValue<T>::SafeValue(T) [with T = bool]
manager.cpp:4: error:   initializing temporary from result of ‘SafeValue<T>::SafeValue(T) [with T = bool]

Now, the issue disappears if I do this:

  //SafeValue(SafeValue<T> &other) : x(other), lock() {}
  SafeValue(const SafeValue<T> &other) : x(other.x), lock() {}

but that isn't thread-safe, and there's no way for me to lock a const SafeValue<T>.

Any ideas?

kazzmir
Member #1,786
December 2001
avatar

SafeValue<bool> Manager::executing;
executing = false;

Something is strange about using = when you declare an object, but I dont remember what it is. Anyway, that works.

Kibiz0r
Member #6,203
September 2005
avatar

I think false is being implicitly converted to SafeValue<bool>... make SafeValue(T y) explicit?

Kitty Cat
Member #2,815
October 2002
avatar

Defining 'Type foo = bar;' Will call Type's constructor with the 'bar' as the parameter. However, you only have a SafeValue<T>& as a contructor argument, and 'false' can't be converted to a reference. You can try having
SafeValue(SafeValue<T> other) : x(other), lock() {}
in addition to the constructors you have and hope the compiler is smart enough to select a reference or non-reference given a parameter. Or maybe add
SafeValue(const T &_x) : x(_x), lock() {}
instead.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Billybob
Member #3,136
January 2003

Quote:

should be fine

That's the code I'd like to use, because it is thread safe, but it won't work because x(other) calls the casting operator of other which is not const. :(

Kitty Cat
Member #2,815
October 2002
avatar

See my edits. I post too fast. :(

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Billybob
Member #3,136
January 2003

:( I've tried a whole bunch of different constructors now with no success. (except, of course, the ones that aren't thread-safe).

It seems that no matter what, the compiler wants to use the copy constructor here:
SafeValue<bool> Manager::executing = false;
Goodness knows why. And I can't make a copy constructor that works there. const won't let me do any thread-safe things, and no const causes a compiler error. :-/

Kibiz0r
Member #6,203
September 2005
avatar

Quote:

I think false is being implicitly converted to SafeValue<bool>... make SafeValue(T y) explicit?

kazzmir
Member #1,786
December 2001
avatar

Uhm.. you are still having a problem? Did you notice my post?

Billybob
Member #3,136
January 2003

Quote:

Uhm.. you are still having a problem? Did you notice my post?

Yes, but my goal is to make the class seamless.

Quote:

I think false is being implicitly converted to SafeValue<bool>... make SafeValue(T y) explicit?

Sorry for being dense but, how would I do that?

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

I think false is being implicitly converted to SafeValue<bool>

The problem is, it isn't. The compiler recognizes that false can be used to instantiate a SafeValue<bool> object which can then be passed to the correct constructor, however there is no explicit object and thus can't be passed as a reference.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Kibiz0r
Member #6,203
September 2005
avatar

Quote:

Sorry for being dense but, how would I do that?

explicit SafeValue(T y)

Quote:

The problem is, it isn't. The compiler recognizes that false can be used to instantiate a SafeValue<bool> object which can then be passed to the correct constructor, however there is no explicit object and thus can't be passed as a reference.

I'm assuming that this is what's really going on:

SafeValue<bool> Manager::executing(SafeValue<bool>(false));

If that's right, what you're saying makes sense -- but I don't see how what I proposed is at odds with what you've said.

Making it explicit would make the call into this:

SafeValue<bool> Manager::executing(false);

(At least, I think so.)

And that is the intended result, isn't it?

Also,

Quote:

but that isn't thread-safe, and there's no way for me to lock a const SafeValue<T>.

I don't know thread-safety very well, but I do know you can make the lock mutable so you can perform non-const operations on it in a const context.

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

I'm assuming that this is what's really going on:
SafeValue<bool> Manager::executing(SafeValue<bool>(false));

No, the problem is it's not doing that. It's basically doing:
SafeValue<bool> Manager::executing(false);
and the compiler complains that there's no constructor that can take a bool. It recognizes it can take a SafeValue<bool>&, but 'false' can't be converted to that. The class would have to have a constructor that can take the template type. A constructor like this:
SafeValue(const T &_x) : x(_x), lock() {}
would allow

SafeValue<bool> Manager::executing(false);
// and
SafeValue<bool> Manager::executing = false;

to work.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

CGamesPlay
Member #2,559
July 2002
avatar

Not related to your problem, but important nevertheless: this class does not make the variable thread-safe. You will still have race conditions:

if(executing)
{
    executing = false;
    // stop the execution
}

The read could occur, then another thread could modify the value before you set the value yourself.

The only way to avoid that is to hold the lock until you are done with it. I recommend this sort of construct:

#define lock(obj, block) \
    obj.lock(); \ 
    try { block } \ 
    finally { \ 
    obj.unlock(); \ 
    } 

lock(executing,
    if(executing)
    {
        executing = false;
        // stop executing
    }
)

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Kibiz0r
Member #6,203
September 2005
avatar

CGames: What is "finally"?

Tobias Dammers
Member #2,604
August 2002
avatar

finally defines a block that is executed after a try-catch structure, no matter whether an exception has occurred or not. The general structure is:

try {
  // this executes until an exception is thrown
}
catch (exception foo) {
  // this executes only if a certain exception was thrown
}
finally {
  // this executes either after the try block finishes without exceptions, or
  // after all catch () blocks have been checked.
}

I'm not 100% positive whether 'finally' is Java-only or if C++ has it too.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

CGamesPlay
Member #2,559
July 2002
avatar

Ah, yeah. C++ you have to do this:

catch(...) {
    obj.unlock();
    throw;
}

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Tobias Dammers
Member #2,604
August 2002
avatar

Hm, but that wouldn't unlock the object if all goes well, would it? One would have to add another obj.unlock() after the last catch block.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

CGamesPlay
Member #2,559
July 2002
avatar

Ah, again, you are right :)
How annoying. I suppose the best way then reads thus:

#define lock(obj, block) { \
    obj.lock(); \ 
    try { \ 
    { block } \ 
    obj.unlock(); \ 
    } \ 
    catch(...) { \ 
    obj.unlock(); \ 
    throw; \ 
    } } 

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Tobias Dammers
Member #2,604
August 2002
avatar

I'd say:

#define lock(obj, block) { \
    obj.lock(); \  
    try { \ 
        block \ 
    } \ 
    catch(...) { \ 
        obj.unlock(); \ 
        throw; \ 
    } \
    obj.unlock(); \ 
}

But I guess that's just personal taste.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

Kibiz0r
Member #6,203
September 2005
avatar

Could rip off the Boost scoped_lock model and make a stack object that unlocks it when it gets destructor'd.

Billybob
Member #3,136
January 2003

Oh C++, why can't you be like Java? Or Java, why can't you not suck?

I'm probably just going to scrap this idea and go back to regular brute-force locking :'(

Thanks for all the help though. It's much appreciated :)

axilmar
Member #1,204
April 2001

Quote:

Whenever the value needs to be accessed a lock is put on it. So I can seamless access the value as if it were the original primitive, and yet it's completely thread-safe.

...

but that isn't thread-safe, and there's no way for me to lock a const SafeValue<T>.

Any ideas?

Copy constructors require the source object to be const; it's a C++ standard.
You should use the keyboard 'mutable' to make the 'lock' member non-const, so you can lock it to get its value. For example:

1#ifndef SAFE_VALUE_H_
2#define SAFE_VALUE_H_
3 
4#include <QReadWriteLock>
5 
6// Thread-safe access to a primative.
7template <typename T>
8class SafeValue
9{
10private:
11 T x;
12 mutable QReadWriteLock lock;
13
14public:
15 SafeValue() : lock() {}
16 SafeValue(T y) : x(y), lock() {}
17 SafeValue(const SafeValue<T> &other) : lock() {
18 operator = (other);
19 }
20
21 SafeValue &operator= ( const T &y ) {
22 lock.lockForWrite();
23 x = y;
24 lock.unlock();
25 return *this;
26 }
27
28 operator T() {
29 T y;
30 lock.lockForRead();
31 y = x;
32 lock.unlock();
33 return y;
34 }
35 
36 SafeValue<T> &operator = (const SafeValue<T> &other) {
37 lock.lockForWrite();
38 other.lock.lockForRead();
39 x = other.x;
40 other.unlock();
41 lock.unlock();
42 return *this;
43 }
44};
45 
46#endif /*SAFE_VALUE_H_*/

I have coded the assignment operator for you as well, just to be on the safe side.

Here is the synchronized statement for C++:

http://www.allegro.cc/forums/thread/553802

Billybob
Member #3,136
January 2003

Wow, I never knew about the mutable keyword. That'll come in handy! Thanks a lot axilmar :) Awesome article on synchronized too. ;)

Kibiz0r
Member #6,203
September 2005
avatar

Quote:

Wow, I never knew about the mutable keyword.

You would if you didn't ignore my posts... :-/

Quote:

I don't know thread-safety very well, but I do know you can make the lock mutable so you can perform non-const operations on it in a const context.

 1   2 


Go to: