 |
|
 |
Hi,
nice, still I have a question about how you handle derived classes.
Imagine your derived class needs parameters on instantiation, here "int foo"
class Derived: public Base
{
public:
Derived(const Base::ForbidConstruction& dummy, int foo) : Base(dummy)
{
};
};
So this would force me to add a new static method to my base class, like this one
template <typename T>
static boost::shared_ptr<T> create(int f)
{
return boost::shared_ptr<T>( new T( Base::ForbidConstruction(), f ) );
}
So for each signature of a derived class constructor I would need a create method that wraps the needed parameters (and that can be quite troublesome).
Am I missing something here? Is there template magic that could help me here?
Thanks
Christian
|
|
|
|
 |
|
 |
Hi, Christian !
Yes, There is some template magic. I already answered this question to someone below.
Here is the answer:
In order to support more Derived class constructor parameters you can right another create() template methods
with more templated parameters, like below. ('[' is "less" and ']' is "greater" )
template [typename T, typename P1]
static SmartPtr[T] create(P1 p1)
{
return SmartPtr[T](new T( Base::ForbidConstruction(), p1) );
}
template [typename T, typename P1, typename P2]
static SmartPtr[T] create(P1 p1, P2 p2)
{
return SmartPtr[T] new T( Base::ForbidConstruction(), p1, p2 ) );
}
SmartPtr[Derived] d1 = Base::create[Derived](1);
SmartPtr[Derived] d2 = Base::create[Derived](1, 3.0);
Regards,
Gregory
|
|
|
|
 |
|
 |
sorry, missed that. Thanks for your answer, great!
|
|
|
|
 |
|
 |
Good article, but something does not convince me ...
See my post below.
|
|
|
|
 |
|
 |
Well ... this is one of many article "out there" that tries to "fix" some C++ weakness.
What makes me wonder here are some "a priori" questions:
- Are them real weakness?
- Is the fix not creating other problem elsewhere ?
The most of OOP languages makes a clear distinction between "value types" (that exist into "scopes", may be on stack or as someone else membeer) and "reference types" (that must exist on heap). This clear dichotomy makes memory management relatively easy.
C++ doesn't: everything can be either an object or a value, and -if you have a reference- there is no way to understand which nature it has. Will tho object i'm looking at, suddenly disappear without my knowledge?
All workarounds (from reference counting, to stack-binded pointers and RAII, to single point construction) are imperfect: they solve the problem by removing feature that have their own reason to exist for other purposes.
Reference counting requires to distinguish from forward to backward pointers (to avoid loops): it eliminates the "value or reference type" dilemma, introducing another one. Is it simpler? Yes, until the context is not too wide compelling with too many interacting developers.
RAII elimitnates the dilemma at the source, by making everything behaving as if leaving on a stack. "The destroyer is the creator". But not everything in the world is like a stack: whatever application gives to a user a "delete" command must be prepared to manage object not required to exist anymore. And saying they have to continue to keep a "secret life" just because their "creator" (the GUI task) isn't dead anymore is a wasting of resource.
Even garbage collected languages -that solve the problem of managing memory- fall into the problem of "finalization of resources": should I keep a handle "open" until it exist? probably no.
In your case, you solve a problem forcing the programmer to adhere to a discipline. But what is the cost of it? How much has a programmer to type more (and repeat annoying definitions) just to avoid the danger of a new X(abcd) left alone, in a context where all API are in term of shared_ptr<X> ?!? May be the risk is not that hight to justify all that.
Just don't use yourself pointesr wherever a smart pointer is given to you by someone, and don't give to other raw pointers: just give them the appropriate smart pointer that definse the wanted destruction semantic. (shared, auto, stack, unique, copy on write, ... whatever is needed)
2 bugs found.
> recompile ...
65534 bugs found.
|
|
|
|
 |
|
 |
Emilio Garavaglia wrote: In your case, you solve a problem forcing the programmer to adhere to a discipline. But what is the cost of it? How much has a programmer to type more (and repeat annoying definitions) just to avoid the danger of a new X(abcd) left alone, in a context where all API are in term of shared_ptr ?!? May be the risk is not that hight to justify all that.
Just don't use yourself pointesr wherever a smart pointer is given to you by someone, and don't give to other raw pointers: just give them the appropriate smart pointer that definse the wanted destruction semantic. (shared, auto, stack, unique, copy on write, ... whatever is needed)
I certainly agree that sometime there is a dilemma whether to limit programmer's freedom and achieve more robust code or to give the programmer more freedom at the expense of possible violation mistakenly done by the programmers. In my case few experienced programmers develop the framework, however extending of the framework by subclassing is handed to plenty of less experenced programmers that are assumed to make certain code mistakes that are not protected by the compiler. So it is important for the framework developers to setup certain discipline integrated within the framework design and thus prevent less experenced programmers from doing some accidental mistakes that will later cause application instability and extra debug cycles.
Programmers do not need to type much more, they just have to define one extra parameter in each derived class constructor. This is absolutely not high price for enforcing application stability.
|
|
|
|
 |
|
 |
I see what you are saying... Nevertheless, in the scope of large-scale applications it's the discipline who is the winner.
-- by experience.
--
Arman
|
|
|
|
 |
|
|
 |
|
 |
It's the most brilliant and interesting solution,
I've ever seen!!!
|
|
|
|
 |
|
 |
Hi Gregory,
it is a nice sharing from you !
Can it be flexible and enough economical -
for more derivations with different constructors' parameters - as well ?
Thank you,
Eugen
They sought it with thimbles, they sought it with care;
They pursued it with forks and hope;
They threatened its life with a railway-share;
They charmed it with smiles and soap.
|
|
|
|
 |
|
 |
Eugen,hi !
In order to support more Derived class constructor parameters you can right another create() template methods
with more templated parameters, like below. ('[' is "less" and ']' is "greater" )
template [typename T, typename P1]
static SmartPtr[T] create(P1 p1)
{
return SmartPtr[T](new T( Base::ForbidConstruction(), p1) );
}
template [typename T, typename P1, typename P2]
static SmartPtr[T] create(P1 p1, P2 p2)
{
return SmartPtr[T] new T( Base::ForbidConstruction(), p1, p2 ) );
}
SmartPtr[Derived] d1 = Base::create[Derived](1);
SmartPtr[Derived] d2 = Base::create[Derived](1, 3.0);
Regards,
Gregory
|
|
|
|
 |
|
 |
Smart and robust solution. Thanks.
I just want to share my experience of using smart pointers with objects allocated on stack.
I use lockable reference counter (this means the reference counting can be disabled by assigning to the counter some special value (-1)) integrated in the object itself. The situation when it is requiered to disable is the fact that the object is allocated on stack.
This fact can be easily detected on Windows like this:
(this code works for VS2008)
#pragma warning(push)
#pragma warning(disable:4035) _inline const PNT_TIB GetTIB() { #ifdef _USE_ASM
_asm MOV EAX, dword ptr FS:[18h];
#else
return (PNT_TIB)__readfsdword(0x18);
#endif
}
#pragma warning(pop)
static __forceinline
bool __is_on_stack(const void* p)
{
register const PNT_TIB tib(GetTIB());
if (p > tib->StackBase)
return false;
return p > tib->StackLimit;
}
so you can add to the constructor of the base class, where the reference counter is implemented, this piece of code:
if (__is_on_stack(this))
LockCounter();
hallelujah
|
|
|
|
 |
|
 |
Does this work with GCC? __forceinline can't gaurentee it will be inlined. Compiler will ignore if it's not good to do it. Register is useless in C++ nowadays.
|
|
|
|
 |
|
 |
You shouldn't be declaring functions and variables with double underscore '__' prefix, as it suggests that the implementation is internal to the compiler or SDK (e.g. from M$ or GNU).
Also, using M$ instrinsics is very bad practice as it's not portable, and also it works only for Win32 executables, and doesn't with x64 execs.
Regards
|
|
|
|
 |
|
|
 |
|
 |
I appreciate the way you spell out the challenges you're attempting to address early on in the article. I also appreciate the fact that these challenges are real, unavoidable, and easily definable. Finally, I find it refreshing to read something that basically offers automatic garbage collection, but doesn't rely on any high-tech, out-of-thread "management." Pre-Java, pre-.NET "unmanaged" memory management does not have to consist of a mess of (easily forgettable) "delete" operations executing at the whim of the programmer. Basic computer science offers a neat alternative, in the form of the call stack and its unwinding.
|
|
|
|
 |