nass nass - 2 months ago 10
C++ Question

c++ variadic template constructor initialization list of members and mixins

I am trying to initialize a variable number of mixins (

ThresholdSensor<>
,
TrendSensor
,
EdgeSensor
) from my host class (
BaseSensor
) that also happen to require as constructor argument, a member of the host class (
BaseSensor::bsd
). I fail in both tasks miserably :)

I am using c++11 , gcc 4.7.1 (so no "emplace" for the std::map) and the code is the following:

#include <iostream> // std::cout std::endl
#include <string> // std::string
#include <map> // std::map

using namespace std;

struct BaseSensorData
{
BaseSensorData(const string & _sensorName)
: sensorName(_sensorName)
{}

const string sensorName;
};

class TrendSensor //mixin for BaseSensor
{
public:
TrendSensor( BaseSensorData & bsd , size_t _windowSize , const double & _trainRangefactor)
{
cout << "TrendSensor: " << _windowSize << " " << bsd.sensorName << " " << _trainRangefactor << endl;
}
};

typedef struct{ double trough; double peak; } Edges;
class EdgeSensor //mixin for BaseSensor
{
public:
EdgeSensor( BaseSensorData & bsd , size_t _windowSize , const double _rangeFactor)
{
cout << "EdgeSensor: " << _windowSize << " " << bsd.sensorName << " " << _rangeFactor << endl;
}
};

class CO2Threshold //mixin for ThresholdSensor
{
std::map<std::string , double>thresholds;

public:
CO2Threshold( const double & _toxicThres , const double & _zeroThres , const double & )
{
thresholds["toxic"] = _toxicThres;
thresholds["zero"] = _zeroThres;
cout << "CO2Threshold: " << _toxicThres << " " << thresholds["zero"] << endl;
}
};
class O2Threshold //mixin for ThresholdSensor
{
std::map<std::string , double>thresholds;

public:
O2Threshold( const double & _toxicThres , const double & _lowThres , const double & _elecChemThres )
{
thresholds["toxic"] = _toxicThres;
thresholds["low"] = _lowThres;
thresholds["elecchem"] = _elecChemThres;
cout << "O2Threshold: " << _toxicThres << " " << thresholds["low"] << " " << thresholds["elecchem"] << endl;
}
};

template<typename ThresholdMixin> //CO2Threshold , O2Threshold , or others ...
class ThresholdSensor : public ThresholdMixin //mixin for BaseSensor
{
public:
ThresholdSensor ( BaseSensorData & bsd
, const size_t & _windowSize
, const double & _toxicThres)
: ThresholdMixin
( _toxicThres //3x the same arg
, _toxicThres //just for the sake
, _toxicThres //of this exercise
)
{
cout << "ThresholdSensor: " << _windowSize << " " << bsd.sensorName << " " << endl;
}

};

template<typename ... SensorType>
class BaseSensor : public SensorType ... //to my BaseSensor class
{
BaseSensorData bsd;

public:
BaseSensor(const string& _sensorName , size_t _windowSize , const double d)
:
bsd(_sensorName) //this causes "Wreorder" warning.
,
SensorType( bsd , _windowSize , d )...
{
cout << "BaseSensor: " << _windowSize << " " << bsd.sensorName << " " << endl;
}

};

int main() {
BaseSensor<ThresholdSensor<CO2Threshold> , TrendSensor, EdgeSensor> bs{string("test"),60U , 14.5f};
//BaseSensor<EdgeSensor> bs2;
}


Focusing on
BaseSensor
, I run the executable with
gdb
and I realised that there is no way to get the "bsd" member to initialise first. The "SensorType" mixins will be init first. So how can I init the bsd first?

Secondly, going through the execution step by step, I realised that execution steps only into "ThresholdSensor" and never in "TrendSensor" or "EdgeSensor". Why is that? I though all mixins would be evaluated with the ellipsis syntax. Would
initializer_list
help with the init ordering somehow? Would
initializer_list
still be the proposed solution if I included the info that ultimately the BaseSensor mixins will have a different number (and type) of arguments and that each mixin will require different values for these arguments.. ?

Thank you in advance for your help

Answer

I don't like very much the following solution but...

First: you can modify the SensorType classes to receiving (as first argument of the constructor) a pointer to BaseSensorData instead of a reference; by example, TendSensor become

class TrendSensor //mixin for BaseSensor
 {
   public:
      TrendSensor (BaseSensorData * pBsd, size_t _windowSize,
                   const double & _trainRangefactor)
       { std::cout << "TrendSensor: " << _windowSize << " "
            << pBsd->sensorName << " " << _trainRangefactor << std::endl; }
 };

Second: you can modify BaseSensor deriving it from BaseSensorData (important: before the SensorType... pack) and deleting the bsd member; BaseSensor become

template <typename ... SensorType>
class BaseSensor : public BaseSensorData, public SensorType ...
 {
   public:

      BaseSensor (const std::string & _sensorName, size_t _windowSize,
                  const double d)
         : BaseSensorData(_sensorName), SensorType(this, _windowSize, d )...
            { std::cout << "BaseSensor: " << _windowSize << " "
              << sensorName << " " << std::endl; } 
 };

The full example follows

#include <iostream> // std::cout std::endl
#include <string>   // std::string
#include <map>      // std::map

struct BaseSensorData
 {
   BaseSensorData (const std::string & _sensorName)
      : sensorName(_sensorName)
    {}

   const std::string sensorName;
 };

class TrendSensor //mixin for BaseSensor
 {
   public:
      TrendSensor (BaseSensorData * pBsd, size_t _windowSize,
                   const double & _trainRangefactor)
       { std::cout << "TrendSensor: " << _windowSize << " "
            << pBsd->sensorName << " " << _trainRangefactor << std::endl; }
 };

typedef struct { double trough; double peak; } Edges;

class EdgeSensor  //mixin for BaseSensor
 {
   public:
      EdgeSensor (BaseSensorData * pBsd, size_t _windowSize,
                  const double _rangeFactor)
       { std::cout << "EdgeSensor: " << _windowSize << " "
            << pBsd->sensorName << " " << _rangeFactor << std::endl; }
 };

class CO2Threshold //mixin for ThresholdSensor
 {
   private:
      std::map<std::string, double> thresholds;

   public:
      CO2Threshold (const double & _toxicThres, const double & _zeroThres,
                    const double & )
       {
         thresholds["toxic"] = _toxicThres;
         thresholds["zero"] = _zeroThres;
         std::cout << "CO2Threshold: " << _toxicThres << " "
            << thresholds["zero"] << std::endl;
       }
};

class O2Threshold //mixin for ThresholdSensor
 {
   private:

      std::map<std::string , double> thresholds;

    public:

      O2Threshold (const double & _toxicThres, const double & _lowThres,
                   const double & _elecChemThres)
       {
         thresholds["toxic"] = _toxicThres;
         thresholds["low"] = _lowThres;
         thresholds["elecchem"] = _elecChemThres;
         std::cout << "O2Threshold: " << _toxicThres << " "
            << thresholds["low"] << " " << thresholds["elecchem"]
            << std::endl;
       }
 };

template <typename ThresholdMixin> //CO2Threshold , O2Threshold , or others ...
class ThresholdSensor : public ThresholdMixin  //mixin for BaseSensor
 {
   public:
      ThresholdSensor (BaseSensorData * pBsd, const size_t & _windowSize,
                       const double & _toxicThres)
         : ThresholdMixin
            ( _toxicThres, //3x the same arg
              _toxicThres, //just for the sake
              _toxicThres)  //of this exercise
       { std::cout << "ThresholdSensor: " << _windowSize << " "
         << pBsd->sensorName << " " << std::endl; }
 };

template <typename ... SensorType>
class BaseSensor : public BaseSensorData, public SensorType ...
 {
   public:

      BaseSensor (const std::string & _sensorName, size_t _windowSize,
                  const double d)
         : BaseSensorData(_sensorName), SensorType(this, _windowSize, d )...
            { std::cout << "BaseSensor: " << _windowSize << " "
              << sensorName << " " << std::endl; } 
 };

int main()
 {
   BaseSensor<ThresholdSensor<CO2Threshold>,
      TrendSensor, EdgeSensor> bs { std::string("test"), 60U, 14.5f };

   //BaseSensor<EdgeSensor> bs2;
 }