|
Boost : |
From: rwgk (rwgk_at_[hidden])
Date: 2002-03-08 01:19:08
--- In boost_at_y..., "David Abrahams" <david.abrahams_at_r...> wrote:
> Suppose you're writing a generic function which has to work for all
> kinds of numeric T:
>
> double, complex<float>, and array_with_shared_semantics<double> are
> examples
>
> You need to create a huge vector<T> at the beginning of the
function,
> and you want to initialize it piecemeal during the function's
execution
> by assigning values into it. You don't want to pay for
initialization if
> you don't have to. Note that you do have to initialize
> array_with_shared_semantics<double>, because its assignment operator
> inspects the current contents. How do you write this?
That was a little exercise. I am happy to present my result,
but I will not fight a battle for this :-)
The basic idea follows the procedure executed by this constructor:
vector(size_t n)
{
// allocator is called here
std::uninitialized_fill_n(begin(), n, T());
size_ = n;
}
Instead of std::uninitialized_fill_n() we have any function
that produces a result of type T, or more generally, a result
that is convertible to a T.
Here is one example (current version of shared_algebra.h):
template<typename ElementType1, typename ElementType2>
inline
shared<
typename binary_operator_traits<
ElementType1, ElementType2>::arithmetic>
operator+(
const shared<ElementType1>& a1,
const shared<ElementType2>& a2) {
typedef shared<
typename binary_operator_traits<
ElementType1, ElementType2>::arithmetic>
result_array_type;
typedef typename result_array_type::value_type
return_element_type;
if (a1.size() != a2.size()) throw_range_error();
result_array_type result(a1.size(), reserve_flag());
array_operation_binary_a_a(functor_plus<
return_element_type,
ElementType1,
ElementType2>(),
a1.begin(), a2.begin(), result.begin(), a1.size(),
has_trivial_destructor<return_element_type>::value());
result.set_size_back_door(a1.size());
return result;
}
has_trivial_destructor will eventually be replaced by some other
trait (see previous posting).
result_array_type result(a1.size(), reserve_flag());
is a desperate attempt to reduce the notational overhead
a little bit and equivalent to:
result_array_type result;
result.reserve(a1.size());
There are two implementations of array_operation_binary_a_a
which are selected via tag dispatching (has_trivial_destructor
is something like int2type<boost::has_trivial_destructor<T>::value>):
array_operation_binary_a_a for non-POD types (roughly speaking)
resembles std::uninitialized_fill_n():
template <typename BinaryFunctorType,
typename ElementType1,
typename ElementType2,
typename ElementTypeResult>
void // not inline
array_operation_binary_a_a(
const BinaryFunctorType& ftor,
const ElementType1* a1,
const ElementType2* a2,
ElementTypeResult* result,
const std::size_t& sz,
false_type) // non-POD result type
{
ElementTypeResult* result_start = result;
try {
ElementTypeResult* result_end = result + sz;
for(;result != result_end; a1++, a2++, result++) {
new (result) ElementTypeResult(ftor(*a1, *a2));
}
}
catch (...) {
cctbx::af::detail::destroy_array_elements(result_start, result,
false_type());
throw;
}
}
The other array_operation_binary_a_a() is much simpler (and inlined):
template <typename BinaryFunctorType,
typename ElementType1,
typename ElementType2,
typename ElementTypeResult>
inline void
array_operation_binary_a_a(
const BinaryFunctorType& ftor,
const ElementType1* a1,
const ElementType2* a2,
ElementTypeResult* result,
const std::size_t& sz,
true_type) // POD result type
{
ElementTypeResult* result_end = result + sz;
for(;result != result_end; a1++, a2++, result++) {
*result = ftor(*a1, *a2);
}
}
Here is the essential secret weapon (and finally the answer
to David's question):
result.set_size_back_door(a1.size());
Without this back door only friend functions could use the
mechanism that I just explained. I have no clue how the
same effect could be achieved without violating the
principle of encapsulation. Personally I have no problem
with this pragmatic approach, but as I said, I am not going
to argue the case with respect to a standard container.
Ralf
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk