Audio ToolKit: Moving to C++17

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...

Audio ToolKit started with only C++11 a long time ago, and now with version 3.1, it’s going to be full C++17.

Let’s start with the problem. In Audio ToolKit, I’m using a set of meta programming functions to enable automatic conversions between types. This enables the user to connect a float input to a double processing unit. This is great because converting is a pain, and I wanted to make it automated.

But in C++11 land, it was quite verbose. For instance here is how the different types were handled and then how scalar conversion worked:

  typedef boost::mpl::vector<std::int16_t, std::int32_t, int64_t, float, double, std::complex<float>, std::complex<double> > ConversionTypes;
 
  template<typename Vector, typename DataType>
  typename boost::enable_if<typename boost::mpl::empty<Vector>::type, void>::type
    convert_scalar_array(ATK::BaseFilter* filter, unsigned int port, DataType* converted_input, gsl::index size, int type)
  {
    throw RuntimeError("Cannot convert types for these filters");
  }
 
  template<typename Vector, typename DataType>
  typename boost::disable_if<typename boost::is_arithmetic<typename boost::mpl::front<Vector>::type>::type, typename boost::disable_if<typename boost::mpl::empty<Vector>::type, void>::type>::type
  convert_scalar_array(ATK::BaseFilter* filter, unsigned int port, DataType* converted_input, gsl::index size, int type)
  {
    convert_scalar_array<typename boost::mpl::pop_front<Vector>::type, DataType>(filter, port, converted_input, size, type - 1);
  }
 
  template<typename Vector, typename DataType>
  typename boost::enable_if<typename boost::is_arithmetic<typename boost::mpl::front<Vector>::type>::type, typename boost::disable_if<typename boost::mpl::empty<Vector>::type, void>::type>::type
    convert_scalar_array(ATK::BaseFilter* filter, unsigned int port, DataType* converted_input, gsl::index size, int type)
  {
    if(type != 0)
    {
      convert_scalar_array<typename boost::mpl::pop_front<Vector>::type, DataType>(filter, port, converted_input, size, type - 1);
    }
    else
    {
      typedef typename boost::mpl::front<Vector>::type InputOriginalType;
      InputOriginalType* original_input_array = static_cast<ATK::TypedBaseFilter<InputOriginalType>*>(filter)->get_output_array(port);
      ATK::ConversionUtilities<InputOriginalType, DataType>::convert_array(original_input_array, converted_input, size);
    }
  }

There are 3 different cases here, the first if there is no more types to test against, the second is a recursion for complex types int he list of convertible types (we don’t care about them, as they are handled by another meta-loop), and then finally the actually workhorse that goes through the different types and does the conversion once the integer type goes to 0 (the value comes from the connected input filter and is also computed through meta-programmation).

So this is very complex and also uses Boost::MPL which was great in C++98, but in modern C++, lots of the tools are standards (checking if a type is arithmetic or not, enable_if…). They were also made more usable, but it lacked the container types. And this comes with MP11, another very recent Boost library.

And check the difference:

  using ConversionTypes = boost::mp11::mp_list<std::int16_t, std::int32_t, int64_t, float, double, std::complex<float>, std::complex<double> > ;
 
  template<typename Vector, typename DataType>
  void convert_scalar_array(ATK::BaseFilter* filter, unsigned int port, DataType* converted_input, gsl::index size, int type)
  {
    if constexpr(boost::mp11::mp_empty<Vector>::value)
    {
      throw RuntimeError("Cannot convert types for these filters");
    }
    else if constexpr(std::is_arithmetic<boost::mp11::mp_front<Vector>>::value)
    {
      if (type != 0)
      {
        convert_scalar_array<boost::mp11::mp_pop_front<Vector>, DataType>(filter, port, converted_input, size, type - 1);
      }
      else
      {
        using InputOriginalType = boost::mp11::mp_front<Vector>;
        InputOriginalType* original_input_array = static_cast<ATK::TypedBaseFilter<InputOriginalType>*>(filter)->get_output_array(port);
        ATK::ConversionUtilities<InputOriginalType, DataType>::convert_array(original_input_array, converted_input, size);
      }
    }
    else // This is in case we add arithmetic types after the non arithmetic ones (should not happen)
    {
      convert_scalar_array<boost::mp11::mp_pop_front<Vector>, DataType>(filter, port, converted_input, size, type - 1);
    }
  }

Much more concise, more readable.

So starting from 3.1, Audio ToolKit will be dropping non C++17 compilers (bye VS2015).

The effort of properly covering branch coverage in this project will also go on, basically by dropping some instantiations when doing code coverage (otherwise the amount of similar tests will just explode for no real gain), and then focusing on also covering the last few missing paths.

Buy Me a Coffee!
Other Amount:
Your Email Address:

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.