Home > Blockchain >  How to get the type underlying std::complex<T> and use it in a class
How to get the type underlying std::complex<T> and use it in a class

Time:05-20

I am writing a data processor, and would like to be able to perform real-to-real, and real-to-complex computations. The setup I have right now:

// class to hold various data types
template <typename T>
class DataArray
{
public:
    DataArray(){};
    T *ptr;
};

// Processing configuration 
template <typename T>
class Config
{
public:
    Config(){};
    T var;
};

// The data processor itself, takes input, output array and the processing configuration
template <typename Tin, typename Tout>
class Process
{
public:
    DataArray<Tin> *in;
    DataArray<Tout> *out;
    Config<Tout> *config;

    Process(){};
    Process(DataArray<Tin> *in_, DataArray<Tout> *out_, Config<Tout> *config_)
    {
        in = in_;
        out = out_;
        config = config_;
    }
};

Currently, I can handle real-to-real processing as follows:

int main()
{
    DataArray<int16_t> input;
    DataArray<float> output;
    Config<float> config;
    // real 2 real processing
    Process<int16_t, float> P1(&input, &output, &config); // works fine 
    return 0;
}

The problem I am facing is that when I want to do real-to-complex processing, I still need my Config to be in the underlying type of std::complex<T>. How can I change the Process class (or any other class) such that I can create a class Process<int16_t, std::complex<float>> that will know that it expects a Config<float>?

int main()
{
    DataArray<int16_t> input;
    DataArray<std::complex<float>> output_complex;
    Config<float> config;

    // real 2 complex processing
    Process<int16_t, std::complex<float>> P2(&input, &output, &config); // Obviously does not work

    return 0;
}

Follow up question Thanks to the Remy's answer, I can now get the type of std::complex in the class Process. If I have a namespace with templated functions, how would I obtain the underlying type in that function/namespace? The following works, but feels a bit clunky. This means that I have to repeat the line using T = typename helper::value_type_of<Tout>::type; in every kernel, am I right?

// namespace with computation kernels
namespace kernels
{
    // define ConfigType in the kernel namespace? 
    template <typename T>
    using ConfigType = Config<typename helper::value_type_of<T>::type>;

    template <typename Tin, typename Tout, bool IQ>
    void bfpw(ConfigType<Tout> *BFC, Tin *RF, Tout *BF)
    {
        // get type of Tout: std::complex<float> or float
        using T = typename helper::value_type_of<Tout>::type;
        T variable = BFC->var;
    }
}

template <typename Tin, typename Tout>
Process<Tin, Tout>::Process(DataArray<Tin> *in_, DataArray<Tout> *out_, ConfigType *config_)
{
    in = in_;
    out = out_;
    config = config_;

    kernels::bfpw<Tin, Tout, true>(config, in->ptr, out->ptr); // call the kernel
}

CodePudding user response:

One way is to define a helper template which Process can use to detect whether Tout is a std::complex or not, and if so then it can use Tout's value_type member, otherwise it can use Tout as-is.

For example:

namespace helper {

    template<typename T>
    struct value_type_of {
        using type = T;
    };

    template<typename T>
    struct value_type_of<std::complex<T>> {
        using type = typename std::complex<T>::value_type;
        // or simply: using type = T;
    };
}

template <typename Tin, typename Tout>
class Process
{
public:
    using ConfigType = Config<typename helper::value_type_of<Tout>::type>;

    ...
    ConfigType *config;

    Process(){};
    Process(..., ConfigType *config_)
    {
        ...
        config = config_;
    }
};

Online Demo

This way, Process<..., float> and Process<..., std::complex<float>> will both accept Config<float>.

  • Related