Hey,

I have a set of types which are all arithmetic (have operators for addition, subtraction, multiplication, etc.). I want to aggregate any number of them into a single object, so I'm using the std::tuple class template and the <tuple> library (and Boost.Tuple for backward compatibility).

My question is a bit of a shot in the dark: Has anyone heard or seen a tuple-like class template that implements all the arithmetic operators?

For example, the std::tuple and boost::tuple classes both implement all the comparison operators which will only compile correctly if all the types contained in the tuple also have the comparison operators in question. I am wondering why there aren't the same provisions for the arithmetic operators?

I could easily implement this myself (but it can amount to a bit of trouble especially for backward compatibility with C++03), but I'm just wondering if anyone heard of one "arithmetic tuple" template that exists already.

Edited 5 Years Ago by mike_2000_17: n/a

Thanks for the reply vijayan, but my question is about tuples of heterogeneous types, not vector-like classes like vararray or TinyVector or many others of the kind.

Here is a more specific example of my problem. Say, I have a bunch of "state-space system" types each with some typedef for the type that represents the state-vector (doesn't have to be a vector), but they are all arithmetic (have + - -= += operators and a few more, like scalar multiplication). Say I have this code (very simplified):

template <typename SystemType>
struct system_traits {
  //..
  typedef typename SystemType::state_diff_type state_diff_type;
  //..
};


//Now, I want to combine many systems together, and for that I need to make composite state descriptors that are also arithmetic:
template <typename... Systems>
struct composite_system {
  //..
  typedef std::tuple< typename system_traits<Systems>::state_diff_type... > state_diff_type;
  //..
};

//Now, I have a concept-check class:
template <typename System>
struct StateSpaceSystemConcept {
  //..
  typename system_traits< System >::state_diff_type dx;

  BOOST_CONCEPT_USAGE(StateSpaceSystemConcept)
  {
    //..
    dx = dx + dx; 
    dx += dx;
    dx -= dx; 
    //.. etc..
  };
};

int main() {
  BOOST_CONCEPT_ASSERT((StateSpaceSystemConcept< composite_system< SystemA, SystemB, SystemA> >));
  //   ERROR: no operator+ for type std::tuple<..> 
  //    .. and so on for other operators.
};

As I said, I can pretty easily write a wrapper for the tuple type to add all those operators, but I was wondering if that didn't exist already and for that matter, why it is not in the std::tuple or boost::tuples::tuple class since other operators are already defined (comparison).

Edited 5 Years Ago by mike_2000_17: n/a

> why it is not in the std::tuple or boost::tuples::tuple class since other operators are already defined (comparison).

IIRC, there was a suggestion to overload the + operator for std::tuple<>. Not for element by element arithmetic plus, but for concatenation of tuples a la std::string.

There would be a similar ambiguity in the case of operator- (element by element minus or symmetric difference?), operator* ( element by element multiply, vector cross product or scalar dot product?).

IMHO, it was best that none of these operators were overloaded by the standard.

> why it is not in the std::tuple or boost::tuples::tuple class since other operators are already defined (comparison).

IIRC, there was a suggestion to overload the + operator for std::tuple<>. Not for element by element arithmetic plus, but for concatenation of tuples a la std::string.

There would be a similar ambiguity in the case of operator- (element by element minus or symmetric difference?), operator* ( element by element multiply, vector cross product or scalar dot product?).

IMHO, it was best that none of these operators were overloaded by the standard.

That makes sense. I understand the ambiguity.

I ended up implementing my own. If anyone is interested or find his way to this thread looking for an arithmetic-tuple class template, I have attached it to this post. My implementation is compatible with both C++11 and C++03 (using Boost Tuples), and I provide it, as is, with no guarantee, of course. Any review or comments on it is welcomed, of course.

Attachments
/**
 * \file arithmetic_tuple.hpp
 * 
 * This library provides the arithmetic tuple class. This class is basically just a wrapper 
 * of either the std::tuple class or the boost::tuples::tuple class, either way, it provides 
 * a meta-programming interface that is equivalent to the wrapped class and with the addition 
 * of the support for all the basic arithmetic operators, requiring, of course, that these 
 * arithmetic operators are also available on all the types contained in the tuple.
 * 
 * \author Sven Mikael Persson <mikael.s.persson@gmail.com>
 * \date September 2011
 */

/*
 *    Copyright 2011 Sven Mikael Persson
 *
 *    THIS SOFTWARE IS DISTRIBUTED UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE v3 (GPLv3).
 *
 *    This file is part of ReaK.
 *
 *    ReaK is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    ReaK is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with ReaK (as LICENSE in the root folder).  
 *    If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef REAK_ARITHMETIC_TUPLE_HPP
#define REAK_ARITHMETIC_TUPLE_HPP

#include "base/defs.hpp"  //For the RK_ENABLE_CXX0X_FEATURES compilation flag.

#ifdef RK_ENABLE_CXX0X_FEATURES
#include <tuple>
#include <type_traits>
#else
#include <boost/tuple/tuple.hpp>
#endif

#include <boost/utility/enable_if.hpp>
#include <boost/mpl/size_t.hpp>
#include <boost/mpl/less.hpp>
#include <boost/mpl/greater.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/prior.hpp>

namespace ReaK {

  
  
namespace detail {
  
/*****************************************************************************************
                             Implementation details
*****************************************************************************************/


  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_addassign_impl( Tuple& lhs, const Tuple& rhs) { };

  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_addassign_impl( Tuple& lhs, const Tuple& rhs) {
    tuple_addassign_impl< boost::mpl::prior<Idx>,Tuple >(lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(lhs) += get<boost::mpl::prior<Idx>::type::value>(rhs);
  };
  
  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx,
      boost::mpl::size_t<0>
    >,
  void >::type tuple_subassign_impl( Tuple& lhs, const Tuple& rhs) { };

  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx,
      boost::mpl::size_t<0>
    >,
  void >::type tuple_subassign_impl( Tuple& lhs, const Tuple& rhs) {
    tuple_subassign_impl< boost::mpl::prior<Idx>,Tuple>(lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(lhs) -= get<boost::mpl::prior<Idx>::type::value>(rhs);
  };
  
  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_mulassign_impl( Tuple& lhs, const Tuple& rhs) { };

  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_mulassign_impl( Tuple& lhs, const Tuple& rhs) {
    tuple_mulassign_impl<boost::mpl::prior<Idx>,Tuple>(lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(lhs) *= get<boost::mpl::prior<Idx>::type::value>(rhs);
  };
  
  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_divassign_impl( Tuple& lhs, const Tuple& rhs) { };

  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_divassign_impl( Tuple& lhs, const Tuple& rhs) {
    tuple_divassign_impl<boost::mpl::prior<Idx>,Tuple>(lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(lhs) /= get<boost::mpl::prior<Idx>::type::value>(rhs);
  };
  
  template <typename Idx, typename Tuple, typename Scalar>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_smulassign_impl( Tuple& lhs, const Scalar& rhs) { };

  template <typename Idx, typename Tuple, typename Scalar>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_smulassign_impl( Tuple& lhs, const Scalar& rhs) {
    tuple_smulassign_impl<boost::mpl::prior<Idx>,Tuple,Scalar>(lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(lhs) *= rhs;
  };
  
  template <typename Idx, typename Tuple, typename Scalar>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_sdivassign_impl( Tuple& lhs, const Scalar& rhs) { };

  template <typename Idx, typename Tuple, typename Scalar>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_sdivassign_impl( Tuple& lhs, const Scalar& rhs) {
    tuple_sdivassign_impl<boost::mpl::prior<Idx>,Tuple,Scalar>(lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(lhs) /= rhs;
  };
  
  
  
  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_add_impl( Tuple& result, const Tuple& lhs, const Tuple& rhs) { };

  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_add_impl( Tuple& result, const Tuple& lhs, const Tuple& rhs) {
    tuple_add_impl<boost::mpl::prior<Idx>,Tuple>(result,lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(result) = get<boost::mpl::prior<Idx>::type::value>(lhs) + get<boost::mpl::prior<Idx>::type::value>(rhs);
  };
  
  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_sub_impl( Tuple& result, const Tuple& lhs, const Tuple& rhs) { };

  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_sub_impl( Tuple& result, const Tuple& lhs, const Tuple& rhs) {
    tuple_sub_impl<boost::mpl::prior<Idx>,Tuple>(result,lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(result) = get<boost::mpl::prior<Idx>::type::value>(lhs) - get<boost::mpl::prior<Idx>::type::value>(rhs);
  };
  
  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_neg_impl( Tuple& result, const Tuple& lhs) { };

  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_neg_impl( Tuple& result, const Tuple& lhs) {
    tuple_neg_impl<boost::mpl::prior<Idx>,Tuple>(result,lhs);
    get<boost::mpl::prior<Idx>::type::value>(result) = -get<boost::mpl::prior<Idx>::type::value>(lhs);
  };
  
  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_mul_impl( Tuple& result, const Tuple& lhs, const Tuple& rhs) { };

  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_mul_impl( Tuple& result, const Tuple& lhs, const Tuple& rhs) {
    tuple_mul_impl<boost::mpl::prior<Idx>,Tuple>(result,lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(result) = get<boost::mpl::prior<Idx>::type::value>(lhs) * get<boost::mpl::prior<Idx>::type::value>(rhs);
  };
  
  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_div_impl( Tuple& result, const Tuple& lhs, const Tuple& rhs) { };

  template <typename Idx, typename Tuple>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_div_impl( Tuple& result, const Tuple& lhs, const Tuple& rhs) {
    tuple_div_impl<boost::mpl::prior<Idx>,Tuple>(result,lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(result) = get<boost::mpl::prior<Idx>::type::value>(lhs) / get<boost::mpl::prior<Idx>::type::value>(rhs);
  };
  
  template <typename Idx, typename Tuple, typename Scalar>
  inline 
  typename boost::enable_if< 
    boost::mpl::equal_to< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_muls_impl( Tuple& result, const Tuple& lhs, const Scalar& rhs) { };

  template <typename Idx, typename Tuple, typename Scalar>
  inline 
  typename boost::enable_if< 
    boost::mpl::greater< 
      Idx, 
      boost::mpl::size_t<0> 
    >,
  void >::type tuple_muls_impl( Tuple& result, const Tuple& lhs, const Scalar& rhs) {
    tuple_muls_impl<boost::mpl::prior<Idx>,Tuple,Scalar>(result,lhs,rhs);
    get<boost::mpl::prior<Idx>::type::value>(result) = get<boost::mpl::prior<Idx>::type::value>(lhs) * rhs;
  };
  
  template <typename Idx, typename Tuple, typename Scalar>
  inline
This question has already been answered. Start a new discussion instead.