Boost logo

Boost :

From: Ralf W. Grosse-Kunstleve (rwgk_at_[hidden])
Date: 2001-12-13 17:47:46


Our work often requires operations on vectors of numeric data.
Unfortunately the STL does not provide adequate support for all
our needs. E.g. valarray (section 26.3.3.1 in the ISO standard)
only defines homogeneous operators such as

  template<class T> valarray<T> operator*
  (const valarray<T>&, const valarray<T>&);

I am thinking about light-weight, general approaches for enabling
element-wise algebraic vector-vector and vector-scalar operations
(e.g. operator+(), operator*(), etc.) on generic sequence types
(e.g. std::vector or boost::array).

I see two possible approaches:

1. Inherit from the "raw" sequence type (e.g. std::vector) and
   define all the operators as members or friends.

2. Define general template operators in a "vector_algebra"
   namespace.

Below are some facts and thoughts about these alternatives. It seems to
me that approach 2 is very general and very powerful, and I am
wondering if the Boost community is interested in advising me how to
refine my current implementation with the goal to have the result
included into Boost.

Ralf

Regarding approach 1:

Since I only want to change the behavior of the class, not the data, it
is quite inconvenient that constructors are not inherited. I came up
with the following scheme for hooking the base class constructors
into the derived class: template constructors:

struct base {
  base() {};
  base(int i) : n(i) {};
  base(int i, int j) : n(i), m(j) {};
  int n;
  int m;
};

struct deri : base {
  deri() {}
  template <class A> deri(A a) : base(a) {}
  template <class A, class B> deri(A a, B b) : base(a, b) {}
};

void foo() {
  deri d0;
  deri d1(3);
  std::cout << d1.n << std::endl;
  deri d2(3, 4);
  std::cout << d2.n << " " << d2.m << std::endl;
}

This works with gcc 3.0.2, Win32 CodeWarrior 7 and Compaq cxx 6.2 (EDG based).
Could there be drawbacks to this approach that I am not aware of?

Regarding approach 2:

There is a prototype implementation at this URL:

http://cci.lbl.gov/~rwgk/tmp/vector_algebra/

  Header files:
    algebra_traits.h
    algebra_operators.h

  Test applications (std::vector and boost::array):
    vector_algebra.cpp

  Python >=1.5.2 scripts that generate the header files:
    generate_vector_algebra_operators.py
    generate_vector_algebra_traits.py

This code works with the compilers listed before. Here is the general
idea: There is a "top-level" template operator such as:

  template <typename TypeLHS, typename TypeRHS>
  typename algebra_traits<TypeLHS, TypeRHS>::promotion_type
  operator+(const TypeLHS& lhs, const TypeRHS& rhs);

The implementation of this operator uses the algebra_traits to select
(at compile time) one of four specialized operators for

  vector + vector
  vector + scalar
  scalar + vector
  scalar + scalar

To enable vector algebra for a given sequence type, algebra_traits
is partially specialized (see top of vector_algebra.cpp):

template <class T, class U>
struct algebra_traits<std::vector<T>, std::vector<U> >
{
  typedef typename algebra_traits<T, U>::promotion_type value_type;
  typedef std::vector<value_type> promotion_type;
  typedef std::vector<bool> bool_type;
  typedef qualifier_v_v type_qualifier;
  template <typename ResultType, typename SizeType>
  static void init(ResultType& v, const SizeType& n) { v.resize(n); }
};

Two similar specializations are required to enable vector-scalar
and scalar-vector operations:

struct algebra_traits<std::vector<T>, U>
struct algebra_traits<T, std::vector<U> >

To enable use of the algebra in a certain scope:

  using namespace vector_algebra;

Note that this enables operators for heterogeneous types, e.g.:

  vector<int> + vector<double>
  vector<double> + int
  etc.

Even the following is possible (see corresponding algebra_traits at the
top of vector_algebra.cpp):

  vector<int> + boost::array<double, 3>
  boost::array<long, 3> + vector<double>
  etc.

Note that the usual operators (such as int + double) are not affected
since the unspecialized algebra_traits dispatches to std::plus etc.

There is one catch, though: operators defined by the sequence type as
member or friend functions (maybe even in the namespace of the sequence
type?) take precedence. E.g., in the case of std::vector, the std
boolean operators (==, !=, >, <, >=, <=) take precedence over the
operators brought in with "using vector_algebra;", but only for
homogeneous expressions such as vector<T> == vector<T>.
An expression such as vector<T> == vector<U> would use the
vector algebra. This can easily lead to confusion. Therefore
the prototype implementation does not define the boolean operators,
but vector_equal_to etc.
Probably, there should be generic names for all the operators,
and separate include files for the actual operator definitions,
e.g.:

#include "algebra_traits.h"
#include "algebra_generic.h"
#include "algebra_arith_ops.h" // operator+ - * / % += -= *= /= %=
#include "algebra_bool_ops.h" // operator== != > < >= <=

__________________________________________________
Do You Yahoo!?
Check out Yahoo! Shopping and Yahoo! Auctions for all of
your unique holiday gifts! Buy at http://shopping.yahoo.com
or bid at http://auctions.yahoo.com


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk