#include <iostream>
#include <cstdint>
#include <complex>

// 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;
};

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;
    };
}

// The data processor itself, takes input, output array and the processing configuration
template <typename Tin, typename Tout>
class Process
{
public:
    using ConfigType = Config<typename helper::value_type_of<Tout>::type>;

    DataArray<Tin> *in;
    DataArray<Tout> *out;
    ConfigType *config;

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

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

void Real2Complex()
{
    DataArray<int16_t> input;
    DataArray<std::complex<float>> output;
    Config<float> config;

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

int main() {
	Real2Real();
	Real2Complex();
	return 0;
}