DGtal 1.4.0
|
Part of the Base package.
This module gathers classes to make easier and more readable parameter passing by expliciting its type in the signature of functions and methods. The main classes are Clone, Alias and ConstAlias. They can be used in conjunction with smart pointer classes CountedPtr, CowPtr, CountedPtrOrPtr, CountedConstPtrOrConstPtr. These classes do not add a significant slowdown (between 0 and 10%) in elementary cases, while they can save 50% of time in some cases (with lazy duplication and move).
Related tests are testCloneAndAliases.cpp, testClone2.cpp.
In the following, the programmer is the one that has written the function/method f
being called, while the user is the one that has written the code that calls f
.
From a C++ point of view, you may pass an object of type T
as parameter to f
through the following mechanism:
f
(T
t
). It is mostly used when passing native types (like int
, bool
, etc), small objects like iterators, as input. It indicates to the user that its argument is duplicated (at least once) and not modified. The user may also give a right-value object as parameter, though without move possibility.f
(const
T&
t
). This form is generally used as an alternative to passing-by-value, to pass bigger objects as input. It may sometimes indicate to the user that the argument is const referenced in f
for further uses, especially when f
is a constructor/method of another object. It is unclear whether the lifetime of the argument should exceed the lifetime of f
. This fact is clarified generally by the documentation.f
(const
T*
ptrT
). This form is sometimes used as an alternative to passing-by-value, to pass bigger objects as input while authorizing an invalid object (null pointer). Although this form is more C-like, it may be used in some C++ situation (like creating or destroying relations between objects). It may sometimes indicate to the user that the argument is const pointed in f
for further uses, especially when f
is a constructor/method of another object. It is unclear whether the lifetime of the argument should exceed the lifetime of f
. This fact is clarified generally by the documentation.f
(T&
t
). It is generally used for modifying an object (input-output or output). It may sometimes indicate to the user that the argument is referenced in f
for further uses and modifications, especially when f
is a constructor/method of an object. It is unclear whether the lifetime of the argument should exceed the lifetime of f
. This fact is clarified generally by the documentation.f
( T*
ptrT
). This form is sometimes used as an alternative to passing-by-reference for modifying an object (input-output or output). Although this form is more C-like, it may be used also for specifying acquisition of the object (which should then have been dynamically allocated). It may sometimes indicate to the user that the argument is pointed in f
for further uses and modifications, especially when f
is a constructor/method of an object. It is unclear whether the lifetime of the argument should exceed the lifetime of f
. This fact is clarified generally by the documentation.The preceding discussion shows that standard parameter passing is ambiguous by nature, since each parameter passing method has several meanings. The user is forced to checked carefully the documentation (at best) or the full code to see how parameters are used.
The programmer should explicit its intent to the user in an unambiguous way directly in the signature of f
. We propose the following taxonomy:
f
, it is not pointed nor referenced for further use, and its lifetime is whatever.f
for further secure use or modifications, and hence its lifetime is whatever.f
for further use. The user is warned that he must adapt the lifetime of the argument consequently (generally the lifetime of the other object). The user knows that its argument will not be modified.f
. This is a cloning for the programmer induced by a specific call by the user with a pointer.f
, it is not pointed or referenced for further use, and its lifetime is whatever. The argument is modified by f
.f
for further use and modifications. The user is warned that he must adapt the lifetime of the argument consequently (generally the lifetime of the other object). The user knows that its argument is shared by another object.Classes Clone, Alias, and ConstAlias are defined in order to accept several arguments from the user. Symmetrically, they may be automatically converted into several types for the programmer. Not all combinations are allowed, but several optimizations are made, most notably by avoiding duplication if possible, by performing lazy duplication, or by moving objects. Furthermore, even for long-term aliasing, the user may choose at compile time between either an unsecure or secure aliasing method, depending on its application.
Therefore, those three classes are useful both from an efficiency and pragmatic point of view.
We propose the following ways to pass parameters without ambiguity:
input parameter | immediate use | immediate cloning / object transfer | long-term const-aliasing |
---|---|---|---|
basic types, small object | f( T t ) | f( Clone<T> t ) | f( ConstAlias<T> t ) |
bigger objects | f( const T& t ) | f( Clone<T> t ) | f( ConstAlias<T> t ) |
User must be careful of argument lifetime | no | no | yes |
input-output / output parameter | immediate use | long-term aliasing / sharing |
---|---|---|
any type/object | f( T& t ) | f( Alias<T> t ) |
User must be careful of argument lifetime | no | yes |
Clone<T*>
. You may use a parameter passing by pointer in the following cases:A parameter ( Clone<T> cT )
accepts several types of arguments constructed from T
. The true behavior of Clone
depends also on the way the programmer uses the object cT
in the function/method. The object cT
may be used to initialize 3 different types of object (often a data member): T
, T*
, CowPtr<T>. The following table sums up the different conversions Argument toward member.
Argument type: | const T& | T* | CountedPtr<T> | CowPtr<T> | T&& (c++11) |
---|---|---|---|---|---|
To:T | Dupl. O(N) | Acq. Dupl. O(N) | Dupl. O(N) | Dupl. O(N) | Move. O(1) |
To:T* | Dupl. O(N) | Acq. O(1) | Dupl. O(N) | Dupl. O(N) | Move. O(1) |
To:CowPtr<T> | Dupl. O(N) | Acq. O(1) | Lazy. O(1)/O(N) | Lazy. O(1)/O(N) | Move. O(1) |
where O(N) stands for the time to duplicate an instance of T
, and with the following abbreviations:
move
constructor for your class.It is clear that worst case is duplication while sometimes Clone is constant time (while guaranteeing object invariance and life-time).
T*
means pointer acquisition. Hence the programmer should take care of deletion of the object. Otherwise, deletion is automatic for T
or CowPtr<T>
member. Furthermore, a conversion to a T*
requires the use of the address operator (operator&
) by the developer. If argument is Clone<T> a
, and member name is b
:member type: | T | T* | CowPtr<T> |
---|---|---|---|
member deletion: | automatic | manual | automatic |
conversion: | automatic: b(a) | address: b(&a) | automatic: b(a) |
Clone<T>
parameter, the programmer should really consider using a CowPtr<T>
member to store it, since it is the most versatile and optimizable variant. The only advantage of the two others storing methods (T
and T*
) is that there is one less indirection.This version acquires the given argument. Note the use of the address operator operator&
and the delete
in the destructor.
A parameter ( Alias<T> cT )
accepts several types of arguments constructed from T
. The true behavior of Alias
depends also on the way the programmer uses the object cT
in the function/method. The object cT
may be used to initialize 3 different types of object (often a data member): T&
, T*
, CountedPtrOrPtr<T>
. The following table sums up the different conversions Argument toward member.
Argument type | T& | T* | CountedPtr<T> | CountedPtrOrPtr<T> |
---|---|---|---|---|
To: T& | Shared. O(1) | Shared. O(1) | ||
To: T* | Shared. O(1) | Shared. O(1) | ||
To: CountedPtrOrPtr<T> | Shared. O(1) | Shared. O(1) | Shared. O(1), secure | Shared. O(1), secure |
Alias<T>
parameter, the programmer should really consider using a CountedPtrOrPtr<T>
member to store it, since it is the most secure-able variant. The only advantage of the two others storing methods (T&
and T*
) is that there is one less boolean test and indirection.T*
requires the use of the address operator (operator&
) by the developer. If argument is Alias<T> a
, and member name is b
:member type: | T& | T* | CountedPtrOrPtr<T> |
---|---|---|---|
conversion: | automatic: b(a) | address: b(&a) | automatic: b(a) |
CountedPtrOrPtr<T>
for storing the alias, the user can choose between a secure long-term aliasing (with a CountedPtr<T>
or CountedPtrOrPtr<T>
argument) or non secure long-term aliasing (with a T&
or T*
argument).A parameter ( ConstAlias<T> cT )
accepts several types of arguments constructed from T
. The true behavior of ConstAlias
depends also on the way the programmer uses the object cT
in the function/method. The object cT
may be used to initialize 3 different types of object (often a data member): T&
, T*
, CowPtr<T>
. The following table sums up the different conversions Argument toward member.
Argument | const T& | const T* | CountedPtr<T> | CountedPtrOrPtr<T> | CountedConstPtrOrConstPtr<T> |
---|---|---|---|---|---|
To: const T& | Shared. O(1) | Shared. O(1) | |||
To: const T* | Shared. O(1) | Shared. O(1) | |||
To: CountedConstPtrOrConstPtr<T> | Shared. O(1) | Shared. O(1) | Shared. O(1), secure | Shared. O(1), secure | Shared. O(1), secure |
ConstAlias<T>
parameter, the programmer should really consider using a CountedConstPtrOrConstPtr<T>
member to store it, since it is the most secure-able variant. The only advantage of the two others storing methods (const
T&
and const
T*
) is that there is one less boolean test and indirection.const T*
requires the use of the address operator (operator&
) by the developer. If argument is ConstAlias<T> a
, and member name is b
:member type: | const T& | const T* | CountedConstPtrOrConstPtr<T> |
---|---|---|---|
conversion: | automatic: b(a) | address: b(&a) | automatic: b(a) |
CountedConstPtrOrConstPtr<T>
for storing the const alias, the user can choose between a secure long-term const-aliasing (with a CountedPtr<T>
or CountedPtrOrPtr<T>
or CountedConstPtrOrConstPtr<T>
argument) or non secure long-term const-aliasing (with a T&
or T*
argument).