Fw: Boost.Threads thread_specific_ptr and more
Hi there.
Rather than respond to your points directly, I've written a document outlining my ideas on how static data could be managed more clearly. This obviously relates to thread_specific_ptr as instances are global, hence static, but is quite general and therefore hopefully more useful.
Regards, Philip Ashmore. ----- Original Message ----- From: "William Kempf"
To: Sent: Friday, March 15, 2002 3:13 PM Subject: Re: Boost.Threads thread_specific_ptr and more From: "Philip Ashmore"
To: Subject: Boost.Threads thread_specific_ptr and more Date: Fri, 15 Mar 2002 14:37:39 -0000 Hi there.
first of all, I'm assuming that it's appropriate to contact you
regarding some queries I have regarding the Boost.Threads thread_specific_ptr<T>.
Yes, though if you'd send it to the Boost Users list on Yahoo Groups
(Boost-Users@yahoogroups.com) instead others could benefit from the answer as well.
As it happens I developed something similar before I became aware of
Boost.Threads thread_specific_ptr<T>, but with the following extra features you might consider discussing or adding.
1. Automatic construction of new instances of T as needed when
dereferenced.
I considered this and rejected it, mostly because this behavior may be surprising to users. Another issue is that this would require T to be default constructable, which will limit the usefulness at least somewhat.
2. Assignment to thread_specific_ptr<T> of a local variable of type T. This is basically already there with reset() but an explicit template class could handle connection and disconnection, coping with exceptions.
Could you elaborate? I'm not sure I understand why you think an assignment operator would be better then reset().
3. An optional Structor class template argument. Here the Structor would know how to create and destroy instances of T, useful when T can be used in different contexts.
This sounds like an Allocator, and if it wasn't a standard Allocator
it could be used to eliminate one of the problems I referred to in (1), but beyond this I don't see any benefit.
4. Automatic cleanup of instances of T when the thread_specific_ptr<T>
goes out of scope. These would be useful when you aren't in control of the thread creation or destruction.
To be portable, thread_specific_ptr<> must be used only to instantiate
global variables (or the equivalent static variants). So, this one can't be done (or more specifically adds 0 benefit).
This relates to static instance management. As you may know, some
compilers (or maybe it's C++ itself) have problems if you have static instances of classes that have non-trivial static data members. The idiom of clients and managers is one solution - manager lifetimes enclose client lifetimes. I've implemented this and it works.
I'm not sure that I'm aware of the problem you're alluding to. Can you
elaborate?
A more general idiom is a dependency tree, where wrapper objects
declare their dependency explicitly and a functor tree is created that is independent of the order of static construction (a map would work here). This tree is traversed during start-up and shutdown (using a scoping object) to ensure correct instantiation and destruction.
Are you referring to issues with the order of construction/destruction
for global data? If you are, there is no universal solution to this problem and I'm not about to try and address it.
It takes over work that the compiler should do correctly but doesn't
(at least not on Visual C++ 6.0 sp5).
This would effectively solve all static-object-dependency-across-libraries issues in a portable way. Maybe this merits a file of its own in Boost.Utility. What do you
Oops! I didn't know I had to join the group!
Regards,
Philip Ashmore
----- Original Message -----
From: "Philip Ashmore"
I think I need more explanation and possibly code examples.
Bill Kempf williamkempf@hotmail.com
[Non-text portions of this message have been removed]
--- In Boost-Users@y..., "Philip Ashmore"
Oops! I didn't know I had to join the group!
It's not a have to kinda thing. I was only suggesting that your questions/comments could be useful to the community and so would be "better" in this forum then just in private e-mail. You do have the choice, and I will respond to personal e-mails :).
Regards, Philip Ashmore ----- Original Message ----- From: "Philip Ashmore"
To: "William Kempf" Cc: Sent: Wednesday, March 20, 2002 12:29 PM Subject: Re: Boost.Threads thread_specific_ptr and more Hi there.
Rather than respond to your points directly, I've written a
document
outlining my ideas on how static data could be managed more clearly. This obviously relates to thread_specific_ptr as instances are global, hence static, but is quite general and therefore hopefully more useful.
Just for other peoples knowledge the attached document can now be found in the files section at http://groups.yahoo.com/group/Boost- Users/files/staticData.doc. I'm not sure that you fully understand the problem your trying to solve by the document's text, so just let me make sure we're on the same page. Your initial example that's supposed to illustrate something that doesn't work, actually does work in some cases. Here's some complete code from your example that will compile, link and run, even using MSVC++ 6 SP 5. #include <iostream> class A { public: A() : val(0) { } int val; }; class B { public: static A g_a; }; A B::g_a; static B g_b; int main(int argc, char* argv) { std::cout << B::g_a.val << std::endl; std::cout << g_b.g_a.val << std::endl; return 0; } The problem you allude to when you say "the order of construction of non-trivial static objects is seemingly arbitrary" actually only applies when the "statics" are defined in different translation units (which you do mention later in the article, so maybe you do understand the issue... I just need to make sure we're on the same page). Section 3.6.2 in the standard defines all of this. This is a well known "problem" for which there isn't any single solution... though there is one solution that's often preferred. Your complex template approach *almost* uses this solution, but not quite. Your templates define smart pointers that lazily create the object on the heap the first time they are needed, and delete the object when the template goes out of scope. There are two problems I see with this. First, using the heap isn't always a good solution. Second, this approach doesn't address the issue with the order of destruction of the objects, which is the inverse problem with the order of construction. The more traditional solution doesn't use the heap and should also address the issue of the order of destruction. All that's needed is to replace the data with a function! A& g_a() { static A instance; return instance; } This takes advantage of the rules for initialization order of local statics. In fact, the recommended way to use call_once() for globals makes use of this technique: A* a = 0; boost::once_flag once_a = BOOST_ONCE_INIT; void init_a() { static A instance; a = &instance; } A& get_a() { boost::call_once(&init_a, once_a); return *a; } Bill Kempf
Hi there.
responding to your points in turn...
For statics defined in different translation units, my solution guarantees
order of construction at a class level, rather than at object level, by
expressing the dependency at class scope. This is the real issue I was
trying to address. The example includes this concept, but not in an explicit
way (it's simple concept that's difficult to express with a simple example).
I guess my document didn't get this across as well as it could have. This
solution allows translation units to be added (or removed) as required while
preserving such relationships.
My solution uses smart pointers to access objects (something the compiler
will hopefully optimise away), but does not define the means by which such
objects are constructed - this is an implementation detail supplied by the
Structor-traits class, which includes the option of using static data
instances in member (or static member or non-member) functions, or
boost::call_once as possible solutions.
If the standard specifies that static objects are destroyed in strict
reverse order to construction then your solution is fine with me, although
(obviously) it doesn't guarantee that the objects are destroyed before
main() exits, which is useful for exception handling, not to mention
debugging.
Regards,
Philip Ashmore.
----- Original Message -----
From: "bill_kempf"
--- In Boost-Users@y..., "Philip Ashmore"
wrote: Oops! I didn't know I had to join the group!
It's not a have to kinda thing. I was only suggesting that your questions/comments could be useful to the community and so would be "better" in this forum then just in private e-mail. You do have the choice, and I will respond to personal e-mails :).
Regards, Philip Ashmore ----- Original Message ----- From: "Philip Ashmore"
To: "William Kempf" Cc: Sent: Wednesday, March 20, 2002 12:29 PM Subject: Re: Boost.Threads thread_specific_ptr and more Hi there.
Rather than respond to your points directly, I've written a
document
outlining my ideas on how static data could be managed more clearly. This obviously relates to thread_specific_ptr as instances are global, hence static, but is quite general and therefore hopefully more useful.
Just for other peoples knowledge the attached document can now be found in the files section at http://groups.yahoo.com/group/Boost- Users/files/staticData.doc.
I'm not sure that you fully understand the problem your trying to solve by the document's text, so just let me make sure we're on the same page. Your initial example that's supposed to illustrate something that doesn't work, actually does work in some cases. Here's some complete code from your example that will compile, link and run, even using MSVC++ 6 SP 5.
#include <iostream>
class A { public: A() : val(0) { } int val; };
class B { public: static A g_a; };
A B::g_a;
static B g_b;
int main(int argc, char* argv) { std::cout << B::g_a.val << std::endl; std::cout << g_b.g_a.val << std::endl; return 0; }
The problem you allude to when you say "the order of construction of non-trivial static objects is seemingly arbitrary" actually only applies when the "statics" are defined in different translation units (which you do mention later in the article, so maybe you do understand the issue... I just need to make sure we're on the same page). Section 3.6.2 in the standard defines all of this. This is a well known "problem" for which there isn't any single solution... though there is one solution that's often preferred. Your complex template approach *almost* uses this solution, but not quite.
Your templates define smart pointers that lazily create the object on the heap the first time they are needed, and delete the object when the template goes out of scope. There are two problems I see with this. First, using the heap isn't always a good solution. Second, this approach doesn't address the issue with the order of destruction of the objects, which is the inverse problem with the order of construction.
The more traditional solution doesn't use the heap and should also address the issue of the order of destruction. All that's needed is to replace the data with a function!
A& g_a() { static A instance; return instance; }
This takes advantage of the rules for initialization order of local statics. In fact, the recommended way to use call_once() for globals makes use of this technique:
A* a = 0; boost::once_flag once_a = BOOST_ONCE_INIT; void init_a() { static A instance; a = &instance; } A& get_a() { boost::call_once(&init_a, once_a); return *a; }
Bill Kempf
Info: http://www.boost.org Wiki: http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl Unsubscribe: mailto:boost-users-unsubscribe@yahoogroups.com
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
participants (2)
-
bill_kempf
-
Philip Ashmore