lundi 7 janvier 2019

Is there a design pattern in CPP that facilitates querying data of different types with a uniform function interface?

I want to provide a single function declaration/definition that returns the correct type of data based on its input argument. This sounds just like what a function template is for, but more specifically, I would like the function interface to look like:

template<class InT>
RetT getData(InT*);

where,

  • Requirement1: RetT depends on the input type InT and is not necessarily equal to InT.
  • Requirement2: In addition, I want to enforce a common interface for all actual InT types to determine what RetT is. In other words, hopefully, InT should be a base class.

A little bit on the background application, let's say I have a text processing system, for which I can specify various configurations. Some configurations might be flags (i.e., boolean values), like performCompact, addSpacing, etc. Some configurations might be tokens (i.e., string literals), like prefixToPath, surfixToLastName, etc. Some configurations might be of custom data types, like textColorMap, fontFamily, etc. I want to provide a common function to query the data of each configuration, rather than one function per configuration. Also, because configuration data are stored in different types, I need this function to return the right type of data depending on what configuration it is used to query. The primary reason for this design choice is to save the maintenance effort. This query infrastructure is in its own component and different teams are working on different subsets of the configurations in their own components. I want to minimize the maintenance effort so that when a new configuration is added, there is no need to modify the query interface.

Some example code is given below, which satisfies both requirements, but I am still wondering if there is a way to avoid class template for Requirement2.

#include <iostream>
using namespace std;

// In a *.hpp file in component A
template<class T>
auto getData(T* spec) {
    return spec->data();
}

template<class DataT>
class Configuration {
    public:
    virtual DataT data() = 0; 
};

// In some other components source files
class PerformCompact : Configuration<bool> {
    private:
    bool _data;
    public:
    PerformCompact(bool d):_data(d){}
    bool data() override {return _data;}

};

class PrefixToPath : Configuration<string>{
    private:
    string _data;
    public:
    PrefixToPath(string d):_data(d){}
    string data() override {return _data;}
};

// In one application source file
int main()
{
   PerformCompact performCompact(true);
   PrefixToPath prefixToPath("Some string");
   auto pd = getData(&performCompact);
   auto pstr = getData(&prefixToPath);
   cout << pd << endl;
   cout << pstr << endl;

   return 0;
}

Aucun commentaire:

Enregistrer un commentaire