Letters to the editor may be sent via email to cujed@cmp.com, or via the postal service to Letters to the Editor, C/C++ Users Journal, 1601 W. 23rd St., Ste 200, Lawrence, KS 66046-2700.
Hello,
I read Tomer Abramsons short article identified above with interest. For the benefit of less experienced developers, however, he seemed to casually gloss over the issues of actually creating the correct type of events required to support his functionality.
For example, in his first example class RWLock, he shows the constructor with a comment /* create events */. At the very least, Tomer should have pointed out that for his RWLock class to work correctly, these events must be created as Auto-Reset events with an initial state of signaled. Otherwise, the Acquire methods simply wait forever.
Kevin Frey
Hi Kevin,
Thank you for your comment. You are definitely right.
The only excuse I have for not supplying the full code for the events creation is that I wanted to keep things as short and simple as possible (which eventually turned out to be too short...).
So, here is the full code for the constructor:
RWLock() : m_nReaders(0) { m_hDataEvent = CreateEvent (NULL, FALSE, TRUE, NULL); hReadersEvent = CreateEvent (NULL, FALSE, TRUE, NULL); }And of course, when you add the m_hAnotherDataEvent event (to support multiple locks), you should create it exactly in the same manner as the two events above.
Regards,
Tomer Abramson
Patrik Tennberg,
One note on your great article on multithreading: in your May 2002 issue of C/C++ Users Journal on page 22, you have a paragraph titled TLS on Other Platforms. The first sentence states: Java has no explicit support for TLS.... As of JDK 1.2, Java does support TLS via its ThreadLocal class.
Suns documentation on the ThreadLocal class is available at: <http://java.sun.com/j2se/1.4/docs/api/java/lang/ThreadLocal.html>.
Before JDK 1.2, you had to simulate TLS as you describe in the article, though.
Nathan Ciliberto
Hi Nathan,
You are right I have spent far too many hours in browser land (where JDK 1.1.8 is king), so I have missed this little gem. Using ThreadLocal classes, you can emulate WIN32 thread-local storage perfectly. We use collection.jar for our browser-based projects, and they give access to all 1.2 collection classes even in 1.1.8.
I was actually reviewing code for a project when I saw that the developer had used ThreadLocal, and I then immediately thought about my oversight.
Thanks for pointing this out.
Patrik Tennberg
Hi,
Reading the Common Knowledge column in the April 2002 issue of CUJ, I found the techniques (e.g., testing for type equality) interesting, but unfortunately found that one part of the column is based on a faulty assumption. Im not certain whether it is a simple error, or that the techniques described in the column encourage such over use (leading to maintenance problems later on). For the MultiIn iterator, it is stated that operator== is fundamentally different depending on whether In1 and In2 are the same type or not. It then gives an implementation of operator== if the types are equal. No explanation is given, and there are many good reasons for not using the special operator==:
- If we have a==b, we should be able to guarantee that ++a==++b, and similarly for -- (if applicable). In fact this holds for neither of the two definitions of operator==. To meet that criteria, operator== should be defined as:
bool operator==(const MultiIn&that) const { return first_==that.first_ && c1_==that.c1__ && e1_==that.e1_ && c2_==that.c2_; }This removes all complex if-statements and works splendidly for comparing against the end of the sequence, and it guarantees that if a==b, then a and b will behave the same.
- One use of MultiIn would be to send the same sequence twice to a function (e.g., copy(multiIn(d1,d1),multiEnd(d1,d1),...);). With the special operator==, it will only be copied once. I would not like that as a user of a template.
- It does not generalize to the case MultiIn<In1,MultiIn<In1,In1> > since In1 and MultiIn<In1,In1> are not the same type. It sort of covers MultiIn<In1,MultiIn<In2,In2> >, but not MultiIn<In1,MultiIn<In1,In2> >!
- It does not generalize to the case where we have conversion operators between the iterators (e.g., MultiIn<T::iterator, T::const_iterator>). I believe that one part of the problem is that it is too easy to make a template specialization behave in other ways (e.g., std::vector<bool> and the above MultiIn<In1,In1>). Basically, we do not always follow some Liskov substitution principle for template specializations. (If a program works for the unspecialized template, it should work the same for the specialized version.) Without such a guarantee, correct programs will suddenly break when adding template specializations. Following such a principle would ensure that multiIn<In1,In1> was only specialized for efficiency and for adding special operations that do not make sense for the general multiIn.
Best regards,
Hans Olsson
Hans,
Thank you for your interesting comments on the article. I would like to re-emphasize that the purpose of the article was not to recommend the use of MultiIn in production code, but to show how easily simple metaprogramming techniques could be applied in a design. However, let me try to justify my approach to the implementation by addressing each of your points below.
1. If we have a==b, we should be able to guarantee that ++a==++b, and similarly for -- (if applicable). In fact this holds for neither of the two definitions of operator==. To meet that criteria, operator== should be defined as:
bool operator==(const MultiIn&that) const { return first_==that.first_ && c1_==that.c1__ && e1_==that.e1_ && c2_==that.c2_; }It is indeed the case that, for a forward iterator, the Standard states that incrementing (or decrementing) iterators that compare equal will result in iterators that still compare equal (though this guarantee does not have to hold for input iterators). However, it is also the case that the Standard guarantees for all iterator types that two iterators will compare equal if and only if the addresses of the elements to which they refer compare equal. The solution you present above satisfies the former requirement but not the latter, whereas mine satisfies the latter but not the former. I would like to claim that my approach is preferable, since it would allow iterators from two different MultiIn sequences to be compared successfully, even if the composed sequences themselves are different. For example, if I were to compose two different MultiIns from three lists one from the first and second list, and one from the second and third list I would like the iterators to compare equal if they referred to the same element of the second list. In fact, neither of our approaches is standard and I cant claim that a MultiIn is ever a forward iterator under either of our implementations, though I can (I believe) still make that claim in the case of the input iterator.
This problem of running into mutually incompatible guarantees seems to be a common one when extending the STL into areas that were perhaps not foreseen during its original design. (For example, in the June issue of CUJ, Common Knowledge features a construct Ive decided to call a fickle-forward iterator, which does not fall squarely into either the forward or the bidirectional iterator category.)
2. One use of MultiIn would be to send the same sequence twice to a function (e.g., copy(multiIn(d1,d1),multiEnd(d1,d1),...);). With the special operator==, it will only be copied once. I would not like that as a user of a template.
Im sorry to say that I have not observed this behavior in any of the implementations of operator = that appeared in the article.
3. It does not generalize to the case MultiIn<In1,MultiIn<In1,In1> >, since In1 and MultiIn<In1,In1> are not the same type. It sort of covers MultiIn<In1,MultiIn<In2,In2> >, but not MultiIn<In1,MultiIn<In1,In2> >!
4. It does not generalize to the case where we have conversion operators between the iterators (e.g., MultiIn<T::iterator, T::const_iterator>).
I dont think its reasonable to expect a MultiIn specialization to generalize to those cases, since a MultiIn<I,I> is a different type from I, and there is no guarantee that a const_iterator type has any association with that of the corresponding non-const iterator other than the required existence of a conversion. The abstract behavior of both the general and specialized versions of MultiIn is (intended to be) identical, and the purpose of the specialization in the case of identical iterator types is purely for efficiency.
This brings us to your final point regarding the danger that template specialization may introduce unintended changes in semantics. I agree that substitutability is a goal with any specialization that is intended to augment an existing public interface, just as it is with run-time polymorphism. Run-time polymorphism is invaluable for leveraging object code (no recompliation necessary) through the addition of substitutable derived classes. Compile-time polymorphism permits similar leveraging of source code (no editing necessary) provided that the overloaded or specialized versions of components fulfill the guarantees specified by the more general versions. I agree also that either form of polymorphism permits extension of the capabilities of the base, provided that the base contract is fulfilled.
Steve Dewhurst
Dear Editor:
On page 60 in CUJ May 2002, David Binner asked about best practices in scientific applications, such as matrix inversion, numerical integration, and root finding.
For matrix inversion, check out <www.research.att.com/~bs/C++.html>, where POOMA from LANL, Blitz++ from University of Waterloo, MTL from University of Notre Dame, and ROOT from CERN are mentioned.
For numerical integration and root finding: check out June 2001 of C/C++ Users Journal.
Thanks,
Qiang Liu
CUJ,
Kudos to both Angelika Langer and Klaus Kreft for their extremely well written and informative April 2002 Java Solutions article (Secrets of equals). The article should be mandatory reading for all Java programmers regardless of experience and skill level.
Pat Paternostro
See the Java Solutions supplement to CUJ this month for Langer and Krefts follow-up article, Implementing equals for Mixed Typed Comparison. ap