Here's a C# version of the behavior I'm trying to achieve:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace baseAbstractTemplates.NET {
interface IObjectTransformer {
TStructure ToStructure<TStructure>(ICollection<byte> bytes);
ICollection<byte> ToBytes<TStructure>(TStructure structure);
};
class ObjectTransformer1 : IObjectTransformer {
#region Implementation of IObjectTransformerBaseAbstractClass
public TStructure ToStructure<TStructure>(ICollection<byte> bytes) {
throw new NotImplementedException();
}
public ICollection<byte> ToBytes<TStructure>(TStructure structure) {
throw new NotImplementedException();
}
#endregion
}
class ObjectTransformer2 : IObjectTransformer {
#region Implementation of IObjectTransformerBaseAbstractClass
public TStructure ToStructure<TStructure>(ICollection<byte> bytes) {
throw new NotImplementedException();
}
public ICollection<byte> ToBytes<TStructure>(TStructure structure) {
throw new NotImplementedException();
}
#endregion
}
class Program {
public static void CoutStructureBytes(IObjectTransformer objectTransformer) {
var bytes = objectTransformer.ToBytes(3);
Console.WriteLine(bytes);
}
static void Main(string[] args) {
ObjectTransformer1 objectTransformer1 = new ObjectTransformer1();
ObjectTransformer2 objectTransformer2 = new ObjectTransformer2();
CoutStructureBytes(objectTransformer1);
CoutStructureBytes(objectTransformer2);
}
}
}
In C# it just works in "haha C# interfaces, templates, polymorphism go brrr" style. Even if you're not familiar with C# at all but have C++ knowledge, I'm sure you can follow that C# code just fine.
This compiles and runs just fine, throws NotImplementedException because not implemented.
But in C++, unlike in C#, I can't just have interfaces with templates, inheritance and polymorphism using usual tools: pure abstract functions(which I override in derived classes), templates, inheritance and method overriding. Because I can't mix method templates with virtual.
After a few days of research I finally found how it's done here:
C++ is Lazy: CRTP - ModernesCpp.com[
^]
CRTP and Static Polymorphism. Finally, C++ version of the behavior I was trying to achieve:
#include <iostream>
#include <vector>
#include <string>
template<typename DerivedClass>
class IObjectTransformer {
public:
template<typename TStructure> TStructure ToStructure(std::vector<unsigned char> bytes);
template<typename TStructure> std::vector<unsigned char> ToBytes(TStructure structure);
private:
IObjectTransformer() = default;
friend DerivedClass;
};
template <typename DerivedClass>
template <typename TStructure>
TStructure IObjectTransformer<DerivedClass>::ToStructure(std::vector<unsigned char> bytes) {
return static_cast<DerivedClass*>(this)->ToStructure(bytes);
}
template <typename DerivedClass>
template <typename TStructure>
std::vector<unsigned char> IObjectTransformer<DerivedClass>::ToBytes(TStructure structure) {
return static_cast<DerivedClass*>(this)->ToBytes(structure);
}
class ObjectTransformer1 : public IObjectTransformer<ObjectTransformer1> {
public:
template <typename TStructure> TStructure ToStructure(std::vector<unsigned char> bytes) {
unsigned char* bytePointer = &bytes[0];
TStructure structure = reinterpret_cast<TStructure>(*bytePointer);
return structure;
}
template <typename TStructure> std::vector<unsigned char> ToBytes(TStructure structure) {
char* bytesArray = reinterpret_cast<char*>(&structure);
auto byteVec = std::vector<unsigned char>(bytesArray, bytesArray + sizeof(TStructure));
return byteVec;
}
};
class ObjectTransformer2 : public IObjectTransformer<ObjectTransformer2> {
public:
template <typename TStructure> TStructure ToStructure(std::vector<unsigned char> bytes) {
TStructure structure{};
std::memcpy(&structure, &bytes[0], sizeof(TStructure));
return structure;
}
template <typename TStructure>
std::vector<unsigned char> ToBytes(TStructure structure) {
std::vector<unsigned char> bytes{};
bytes.resize(sizeof(TStructure));
std::memcpy(&bytes[0], &structure, sizeof(TStructure));
return bytes;
}
};
template <typename DerivedClass, typename TStructure>
void CoutStructureBytes(IObjectTransformer<DerivedClass> *objectTransformerBaseAbstractClass, TStructure structure) {
auto bytes = objectTransformerBaseAbstractClass->template ToBytes<TStructure>(structure);
for(auto byte : bytes) {
std::cout << std::to_string(byte) << ' ';
}
std::cout << std::endl;
}
int main() {
ObjectTransformer1 objectTransformer1{};
ObjectTransformer1 objectTransformer2{};
int integer = 5;
float someFloat = 9.79f;
CoutStructureBytes(&objectTransformer1, integer);
CoutStructureBytes(&objectTransformer2, someFloat);
}
PS I can barely follow this code myself even though I wrote it, but I guess I'll get used to it.