Follwing Problem. I am working on a bigger c project. That project has a central dataClass: mal::FrameAudio
and the data class looks like this:
class FrameAudio {
public:
typedef Eigen::Matrix<unsigned char, Eigen::Dynamic, Eigen::Dynamic> pluginType;
...
const pluginType& getPlug() const;
void setPlug(const pluginType& plugin);
...
Now I want to extend some functionality of the software, which needs a different pluginType
in my case I would need:
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> pluginType;
How can I implement the new dataType without breaking the functionality of the rest of the code?
Inheritance? I thought about doing something like this FrameAudioFloat: public FrameAudio. But the data storage get initialized way earlier in the software, outside of my functionality and as far as I know, when a pointer is set to the base class you can only use the overwritten functions of the derived class. So I cannot call getPlug() since it has another return type.
Template? I was thinking about creating a template class? But than I saw that the code often uses the typedef of the datastorage. You will find something like this very often in the code:
const mal::FrameAudio::pluginType& plugins = aFrame.getPlug();
I even found that typedef in member definitions of other classes. So what is the easiest way for me to extend this class with the new typedef and funcions? For now its sufficient to me if just my functionality works with the new typedef and the rest of the code still works with the old. Maybe use auto as a typedef?
CodePudding user response:
You can not change the type of an object dynamically, C doesn't allow that. So much up front.
Now, using a template is probably the right way to go. Point is, you probably can not change this while keeping the code the same, which is what you're effectively asking for. Instead, you can write similar code but with the unsigned char
replaced with a float
, because that doesn't touch the existing code. In order to avoid actual code duplication, creating a template is the way to go:
template <typename scalar> class FrameAudioT
{
// type from the outer class used in the plugin type
typedef Eigen::Matrix<scalar, Eigen::Dynamic, Eigen::Dynamic> pluginType;
// ...other code...
};
// for compatibility with existing code:
typedef FrameAudioT<char> FrameAudio;
Start like that with the FrameAudio
class. Then, pick the next class which still depends on the legacy, non-templace FrameAudio
type. Once this type isn't used any more, you can remove the typedef.
Of course, in the middle you can refactor the code. For example, if you find your code only uses a subset of the plugin's interface that doesn't depend on its first parameter, you might be able to create a pure virtual baseclass ("interface class") and use that instead. The details depend on your application, so it's hard to give definite advise.
CodePudding user response:
I'd go with templates. You turn your existing code into the base case by defaulting the plugin type to the existing type:
template <class PluginType =
Eigen::Matrix<unsigned char, Eigen::Dynamic, Eigen::Dynamic
>
class FrameAudio {
public:
typedef PluginType pluginType;
...
const pluginType& getPlug() const;
void setPlug(const pluginType& plugin);
};
So when FrameAudio
is used without specifying template arguments it behaves as before, which means existing code and typedefs will all work. Additionally you provide a specialization for the new plugin type:
template <>
class FrameAudio<
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>
>
{
public:
using PluginType = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>;
// You can even access classes of the base template
// if you want to share implementations
....
};
Another nifty trick is to swap the types behind the curtain, e.g. define
using FrameAudio = FrameAudioG<
Eigen::Matrix<unsigned char, Eigen::Dynamic, Eigen::Dynamic>
>;
where FrameAudioG
is the generic version with templatized plugin type (as shown above). This way new users utilize the generic interface where both plugins are accessible and legacy code continues to work like before.