Hi log4cxx developers,
now that I can build the library (thanks for the fix) I've been poking
around and exploring the code.
ObjectPtrT& operator=(const ObjectPtrT& p1) {
T* newPtr = (T*) p1.p;
if (newPtr != 0) {
newPtr->addRef();
}
T** pp = &p;
void* oldPtr = ObjectPtrBase::exchange((void**) pp, newPtr);
if (oldPtr != 0) {
((T*) oldPtr)->releaseRef();
}
return *this;
}
wow ... that's ugly :)
What's the point of the C-style sledgehammer cast on the first line of
this function?
How can p1.p not be convertible to T*? Casts like that do nothing
except hide errors if one day the types do change.
Is there a good reason why ObjectPtrBase::exchange isn't a template,
so it doesn't have to rely on more sledgehammer casts to/from void* ?
I guess that would mean the implementation had to be in the header, so
assuming that's undesirable, I would suggest an
ObjectPtrT<T>::exchange(T**, T*) which does the casts and calls the
base implementation, so that the casts are only needed in one place.
These functions don't need their casts either:
T* operator->() const {return (T*) p; }
T& operator*() const {return (T&) *p; }
operator T*() const {return (T*) p; }
(The last of these functions is a bad idea IMHO, as with most implicit
conversion operators. An explicit ObjectPtrT::get() function would be
better, but that would be an API change, so nevermind.)
This overloaded assignment operator is not const-correct:
ObjectPtrT& ObjectPtrT::operator=(const T* p1);
This allows:
typedef const Foo CFoo;
CFoo* p = new CFoo(some_foo);
ObjectPtrT<Foo> op;
op = p;
*op = another_foo;
assert( *p == some_foo ); // boom!
If there are cases where the const-ness needs to be removed, it should
be done explicitly by the user of ObjectPtrT, since only the user
knows if a particular case is safe or not. Doing it automatically
inside ObjectPtrT hides potentially serious errors.
I removed that assignment operator and successfully built and tested
the lib with make check, so I conclude it's not needed anyway.
If you do need to store a pointer-to-const in an ObjectPtrT you can do:
ObjectPtrT<const Foo> op;
op = p;
Finally, both overloads of ObjectPtrT::operator< compare pointers
using < which is not guaranteed to be portable by the C++ standard,
see 20.3.3 paragraph 8:
"For templates greater, less, greater_equal, and less_equal, the
specializations for any pointer type yield a total order, even if the
built-in operators <, >, <=, >= do not."
Therefore for portability std::less should be used to compare pointers.
The attached patch addresses all these points, could it be considered
for inclusion?
It seems from its implementation that ObjectPtrBase::exchange() is not
atomic on 64-bit UNIX platforms, are there any plans to change that?
Jonathan
Index: src/main/include/log4cxx/helpers/objectptr.h
===================================================================
--- src/main/include/log4cxx/helpers/objectptr.h (revision 620195)
+++ src/main/include/log4cxx/helpers/objectptr.h (working copy)
@@ -19,6 +19,7 @@
#define _LOG4CXX_HELPERS_OBJECT_PTR_H
#include <log4cxx/log4cxx.h>
+#include <functional>
//
// Helgrind (race detection tool for Valgrind) will complain if pointer
@@ -44,7 +45,7 @@
ObjectPtrBase();
virtual ~ObjectPtrBase();
static void checkNull(const int& null);
- static void* exchange(void** destination, void* newValue);
+ static void* exchangeVoidP(void** destination, void* newValue);
virtual void* cast(const Class& cls) const = 0;
};
@@ -109,14 +110,13 @@
}
ObjectPtrT& operator=(const ObjectPtrT& p1) {
- T* newPtr = (T*) p1.p;
+ T* newPtr = p1.p;
if (newPtr != 0) {
newPtr->addRef();
}
- T** pp = &p;
- void* oldPtr = ObjectPtrBase::exchange((void**) pp, newPtr);
+ T* oldPtr = ObjectPtrT::exchange(&p, newPtr);
if (oldPtr != 0) {
- ((T*) oldPtr)->releaseRef();
+ oldPtr->releaseRef();
}
return *this;
}
@@ -127,10 +127,9 @@
// throws IllegalArgumentException if null != 0
//
ObjectPtrBase::checkNull(null);
- T** pp = &p;
- void* oldPtr = ObjectPtrBase::exchange((void**) pp, 0);
+ T* oldPtr = ObjectPtrT::exchange(&p, (T*)0);
if (oldPtr != 0) {
- ((T*) oldPtr)->releaseRef();
+ oldPtr->releaseRef();
}
return *this;
}
@@ -139,26 +138,13 @@
if (p1 != 0) {
p1->addRef();
}
- T** pp = &p;
- void* oldPtr = ObjectPtrBase::exchange((void**) pp, p1);
+ T* oldPtr = ObjectPtrT::exchange(&p, p1);
if (oldPtr != 0) {
- ((T*)oldPtr)->releaseRef();
+ oldPtr->releaseRef();
}
return *this;
}
- ObjectPtrT& operator=(const T* p1) {
- if (p1 != 0) {
- p1->addRef();
- }
- T** pp = &p;
- void* oldPtr = ObjectPtrBase::exchange((void**) pp, const_cast<T*>(p1));
- if (oldPtr != 0) {
- ((T*)oldPtr)->releaseRef();
- }
- return *this;
- }
-
ObjectPtrT& operator=(ObjectPtrBase& p1) {
T* newPtr = reinterpret_cast<T*>(p1.cast(T::getStaticClass()));
return operator=(newPtr);
@@ -171,17 +157,22 @@
bool operator==(const ObjectPtrT& p1) const { return (this->p == p1.p); }
bool operator!=(const ObjectPtrT& p1) const { return (this->p != p1.p); }
- bool operator<(const ObjectPtrT& p1) const { return (this->p < p1.p); }
+ bool operator<(const ObjectPtrT& p1) const { return std::less<const T*>(this->p, p1.p); }
bool operator==(const T* p1) const { return (this->p == p1); }
bool operator!=(const T* p1) const { return (this->p != p1); }
- bool operator<(const T* p1) const { return (this->p < p1); }
- T* operator->() const {return (T*) p; }
- T& operator*() const {return (T&) *p; }
- operator T*() const {return (T*) p; }
+ bool operator<(const T* p1) const { return std::less<const T*>(this->p, p1); }
+ T* operator->() const { return p; }
+ T& operator*() const { return *p; }
+ operator T*() const { return p; }
private:
+ static T* exchange(T** ptr, T* val) {
+ return static_cast<T*>(
+ ObjectPtrBase::exchangeVoidP((void**)ptr, val) );
+ }
+
T * p;
virtual void* cast(const Class& cls) const {
if (p != 0) {
@@ -192,7 +183,6 @@
};
-
}
}
Index: src/main/cpp/objectptr.cpp
===================================================================
--- src/main/cpp/objectptr.cpp (revision 620195)
+++ src/main/cpp/objectptr.cpp (working copy)
@@ -33,7 +33,7 @@
}
}
-void* ObjectPtrBase::exchange(void** destination, void* newValue) {
+void* ObjectPtrBase::exchangeVoidP(void** destination, void* newValue) {
#if _WIN32 && (!defined(_MSC_VER) || _MSC_VER >= 1300)
return InterlockedExchangePointer(destination, newValue);
#elif APR_SIZEOF_VOIDP == 4