Hi, I'd like some help trying to crack a problem I've got. Now, I am using shared pointers at the moment, and that might be part of my problem, or it might not, but I'll describe the situation and you can tell to go elsewhere if you like! :) Part of my application involves scripting - allowing the user to write file based scripts to execute within the context of an application. I've designed it so it also gives the ability for the application writer to create some 'scripts' by creating classes directly in the C++ code. The script has the ability to run in the main thread of the app, but some statements could potentially block, so each statement is run from a 'timer' event which gets scheduled immediately. Those that block wait for an event and continue on that event. This part is working well. The statements are classes constructed in a hierarchical manner with things like a block class (a statement itself) which has a list of statements in it, an if statement class - which has an expression evaluator, a statement for the 'then' part, and an optional statement for the 'else' part. You get the idea? After looking at boost::lambda I thought it would be nice if I could construct my script similar to how lambda does these sorts of control statements - the if_, switch_, for_ constructs. The idea I have is like: expression exp = ... If_statement s1 = if_( expr)->then_( s2 )->else_( s3 ); while_statement ws = while_( expr ).do_( s1 ); One of the things I decided early on was to use shared_ptrs. The while_ and if_ constructs are functions returning a shared pointer to the real if statement object. Now my problem: given the example above. The do_ method of a while_statement takes a shared pointer to a statement so I don't have to worry about deallocation. Constructing the if_ statement on it's own is no problem, but if I was to construct one directly within the do_ part of the while statement it wont compile: while_( expr ).do_( if_(expr)->then_(s2)->else_(s3) ); The problem is to be able to chain then_ and else_ together, I have to return a plain pointer because the if_ statement itself doesn't know about it's shared pointer. So now, the compiler complains that can't implicitly convert a plain if_statement pointer into a shared pointer. And even if it could, I don't have the original shared pointer to hand. Now, I hope you're all still listening at the back! :) My first thought was to somehow get the object to look after the shared pointer, but surely if the object has a reference to itself, the ref count of the shared pointer could never reach zero? As I said right at the beginning, I think is a problem I have with shared pointers, whether it be a misunderstanding about them or maybe I'm using them the wrong way? I could always go for scrapping the shared pointers all together, and make the classes responsible for their own (de)allocation, but I was rather hoping to avoid that. Any tips and suggestions on ways forward will be much appreciated. (Even if to say - "ha! it'll never work", or "try a another newsgroup"! :) TIA. -- Regards, Steve.
On Sun, 08 Feb 2004 01:28:38 +0000, Steve Folly wrote
The problem is to be able to chain then_ and else_ together, I have to return a plain pointer because the if_ statement itself doesn't know about it's shared pointer. So now, the compiler complains that can't implicitly convert a plain if_statement pointer into a shared pointer. And even if it could, I don't have the original shared pointer to hand.
It looks like you can solve this by using intrusive pointers instead of shared pointers. The compiler can implicitly convert normal pointers into intrusive ones. Todd
On 7/2/04 9:37 pm, in article 20040207213053.M73385@www.flemingcnc.com,
"todd"
On Sun, 08 Feb 2004 01:28:38 +0000, Steve Folly wrote
The problem is to be able to chain then_ and else_ together, I have to return a plain pointer because the if_ statement itself doesn't know about it's shared pointer. So now, the compiler complains that can't implicitly convert a plain if_statement pointer into a shared pointer. And even if it could, I don't have the original shared pointer to hand.
It looks like you can solve this by using intrusive pointers instead of shared pointers. The compiler can implicitly convert normal pointers into intrusive ones.
Todd
Thanks for your reply, Todd. Ah - so since my object will always be holding a referece, and I can provide my intrusive_ptr_release function to release when the ref count is 1? Is that what you meant? And all my helper functions will be based on an intrusive pointer, rather than the shared pointer? I've noticed that the shared_ptr mechanism is thread safe, but the intrusive pointer isn't. I don't think that will be a problem because we enforce a policy of only accessing them from one thread anyway. I'll have a play. Cheers, Steve.
On Sun, 08 Feb 2004 09:59:21 +0000, Steve Folly wrote
Ah - so since my object will always be holding a referece, and I can provide my intrusive_ptr_release function to release when the ref count is 1? Is that what you meant?
Most implementations delete when the count falls to 0.
And all my helper functions will be based on an intrusive pointer, rather than the shared pointer?
Yes
I've noticed that the shared_ptr mechanism is thread safe, but the intrusive pointer isn't. I don't think that will be a problem because we enforce a policy of only accessing them from one thread anyway.
Why do you say that? Here's a simplified example (it lacks some border cases
that most people don't need):
============
class ref_counted: boost::noncopyable
{
public:
ref_counted():
num_refs(0)
{
}
virtual ~ref_counted()
{
}
long mutable volatile num_refs;
};
inline void intrusive_ptr_add_ref(const ref_counted* p)
{
thread_safe_increment(p->num_refs);
}
inline void intrusive_ptr_release(const ref_counted* p)
{
if(!thread_safe_decrement(p->num_refs))
delete p;
}
============
Here's how to use it:
class my_class: public ref_counted
{
// ...
};
intrusive_ptr
On 7/2/04 9:37 pm, in article 20040207213053.M73385@www.flemingcnc.com,
"todd"
On Sun, 08 Feb 2004 01:28:38 +0000, Steve Folly wrote
The problem is to be able to chain then_ and else_ together, I have to return a plain pointer because the if_ statement itself doesn't know about it's shared pointer. So now, the compiler complains that can't implicitly convert a plain if_statement pointer into a shared pointer. And even if it could, I don't have the original shared pointer to hand.
It looks like you can solve this by using intrusive pointers instead of shared pointers. The compiler can implicitly convert normal pointers into intrusive ones.
Todd
Hmmm... I've looked at our other posts in this thread and I don't think I've
really explained my problem:
I initially wanted to use shared pointers to not have to worry about memory
deallocation.
[please ignore syntax errors!]
class statement
{
public:
virtual void execute() = 0;
};
typedef shared_ptr<statement> statement_p;
class block_statement : public statement
{
public:
block_statement ();
block_statement_p add( statement_p s )
{
statements.push_back( s );
return ????;
}
private:
std::list
On Sun, 08 Feb 2004 20:47:41 +0000, Steve Folly wrote
Hmmm... I've looked at our other posts in this thread and I don't think I've really explained my problem:
Actually, I think you explained the problem quite well.
I initially wanted to use shared pointers to not have to worry about memory deallocation.
Yes. All smart pointers serve this purpose.
[please ignore syntax errors!]
ditto.
class statement: public ref_counted
{
public:
virtual void execute() = 0;
};
typedef intrusive_ptr<statement> statement_p;
// Look at this trick :)
typedef intrusive_ptr<class block_statement> block_statement_p;
class block_statement : public statement
{
public:
block_statement ();
block_statement_p add( statement_p s )
{
statements.push_back( s );
return this; // conversion is implicit
}
private:
std::list
My dilemma is - what do block_statement::add, if_statement::then and if_statement::else return? Since they have no knowledge of the shared pointer, the best they can do is to return 'this', but block_statement::add is expecting the shared pointer?
intrusive_ptr can be declared on a class that hasn't been defined yet. The
functions shoud return an intrusive_ptr. I think I said earlier that they
could return normal pointers; if so I made a mistake. That *might* cause
premature deletion; the standard doesn't place a strong enough guarantee on
the order of operations. i.e. a temporary intrusive_ptr might be destructed
before another is constructed if those member functions return normal pointers.
The call to add() in the example above automatically converts the
intrusive_ptr
participants (2)
-
Steve Folly
-
todd