The Problem
I want to implement a number of algorithms that work on a graph and return scores for node-pairs indicating whether those nodes are similar. The algorithms should work on a single node-pair and on all possible node-pairs. In the latter case a collection/matrix should be returned.
My Approach
The algorithms derive from
class SimilarityAlgorithm {
public:
Base(const Graph& G);
virtual double run(node u, node v) = 0; // indices for nodes in the graph
virtual ScoreCollection& runAll() = 0;
}
Now the algorithms differ in memory usage. Some algorithms might be symmetric and the scores for (u, v) and (v, u) are identical. This requires different ScoreCollection-types that should be returned. An example would be a sparse-matrix and a triangular matrix that both derive from ScoreCollection.
This would boil down to covariant return types:
class SpecificAlgorithm : SimilarityAlgorithm {
public:
double run(node u, node v);
// The specific algorithm is symmetric and thus uses a symmetric matrix to save memory
SymmetricScoreCollection& runAll();
}
Question
Is this design approach a good idea for this problem?
Should the fact that the collections are all implemented as matrices be exposed?
Your design seems appropriate for the problem you describe.
Problem:
However, there is a problem with your SpecificAlgorithm : runAll() doesn't return the same type as the virtual function of the base class. So it won't be called (or more probably, your code won't compile because of a missing virtual function).
Solution:
Use also a polymorphic approach for the ScoreCollection, by making SymmetricScoreCollection a derived class of ScoreCollection :
class SymetricScoreCollection: public ScoreCollection {
//define the member functions to access the values virtual
...
};
class SpecificAlgorithm : public SimilarityAlgorithm {
public:
double run(node u, node v);
// The specific algorithm is symmetric and thus uses a symmetric matrix to save memory
ScoreCollection& runAll();
};
In fact it's an application of the factory method pattern, with the following roles:
SimilarityAlgorithm is the factory,
SpecificAlgorithm is the concrete factory
ScoreCollection is the product
SymetricScoreCollection is the concrete product
Additional remark:
Returning a reference to ScoreCollection from runAll() brings in some risks. Suppose sa is a specific algorithm.
In the following statement :
ScoreCollection sc = sa.runAll();
sa.runAll() returns a reference to a SymetricScoreCollection, but it would copy the referred object to sc, making it a ScoreCollection. Slicing occurs, and polymorphism will fail to work.
The following statement would however succeed:
ScoreCollection& rsc = sa.runAll();
because rsc is a reference and it would still refer to the original SymetricScoreCollection object returned by sa.runAll(), and everything would work as designed.
You see that it's very easy to have unnoticed mistakes when returning references. I'd suggest to return a pointer instead of a reference.
Related
I'm using two external libraries which define classes with identical contents (let's say Armadillo's Arma::vec and Eigen's Eigen::VectorXd). I would like to be able to convert between these classes as cleanly as possible.
If I had defined either class, it would be trivial to include a constructor or conversion operator in that class' definition, to allow me to write e.g.
Arma::vec foo(/*some constructor arguments*/);
Eigen::VectorXd bar = Eigen::VectorXd(foo);
but since both classes are from external libraries, I cannot do this. If I attemt to write a naive conversion function, e.g.
class A{
public:
int value_;
A(int value) : value_(value) {}
};
class B{
public:
int value_;
B(int value) : value_(value) {}
};
A A(const B& b){return A(b.value_);}
int main(void){
A a(1);
B b(2);
a = A(b);
}
then the function shadows the class definition, and suddenly I can't use the A class at all.
I understand that allowing A a=b to be defined would be a bad idea, but I don't see why allowing A a=A(b) would cause any problems.
My question:
Is it possible to write a function or operator to allow the syntax A a=A(b)? And if not, is there a canonical way of doing this kind of conversion?
I've seen A a=toA(b) in a few libraries, but this isn't used consistently, and I dislike the inconsistency with the usual type conversions.
Is it possible to write a function or operator to allow the syntax A a=A(b)?
No, it is not possible. The two classes involved define what conversions are possible and you can't change a class definition after it has been defined.
You will need to use a function as in your given example, although I would avoid repeating the type name and write
auto a = toA(b);
TL;DR
Best engineering practice is to use design pattern Factory by introducing function (or utility class) that consumes Eigen::VectorXd and returns Arma::vec.
Arma::vec createFrom(Eigen::VectorXd from) { ... }
Any other hacking is a waste of time and introduction of tight coupling that will strike back sooner or later. Loose coupling is essential in SW engineering.
Detailed
You might introduce descendant of the target class where you would define a constructor like you described:
class MyArma : Arma::vec {
public:
MyArma(Eigen::VectorXd from) : x(from.x), y(from.y), z(from.z) {
/* empty constructor as we are fine with initializers */
}
}
Then you'd just be able to create Arma vectors based on Eigen's vecotrs into E.g. Arma typed array
Arma::vec vecArray[] = { MyArma(eigenVect1), MyArma(eigenVect2) };
which comes from the principles of inheritance. Alternatively you could use a design pattern called Decorator where original vector (Eigen) is hidden behind the interface of the current vector (Armadillo). That involves overrding all the methods and there must be no public attribute and all the methods must have been delared as virtual... So lot of conditions.
However there are some engeneering flaws in above design. You are adding a performance overhead with Virtual Method Table, you are getting yourself in maintaining quite big and sensitive library for this purpose. And most important: You'd create technological dependency - so called spaghetti. One object shouldn't be avare about alternatives.
The documentation to armadillo gives nice hint that you should use design pattern called Factory. Factory is a standalone class or a function that combines knowledge of both implementations and contains algorihm to extract information from one and construct the other.
Based on http://arma.sourceforge.net/docs.html#imbue you'd best create a factory class that creates the target vector of the same size as the input vector and using method imbue(...) it would set the values of individual elements based on corresponding elements from the input vector.
class ArmaVecFacotry() {
Arma::vec createFrom(Eigen::VectorXd from) {
Arma::vec armaVec(from.size(), fill::none);
int currentElement = 0;
armaVec.imbue( [&]() { return from(currentElement++); } );
return armaVec;
}
}
and then simply create objects like
Eigen::VectorXd sourceVector;
Arma::vec tergetvector = std::move(ArmaVecFactory::createFrom(sourceVector));
Notes:
You can have currentElement counter outside of the lambda expression as it is captured by [&]
I am creating the vector on stack but std::move outside make sure that the memory is being used effectively without excessive copying.
Let's say I have two different implementations of mathematical vectors (and other mathematical structures such as matrices):
class SparseVector {
double dotproduct(SparseVector& other);
};
class DenseVector {
double dotproduct(DenseVector& other);
};
I would like to implement algorithm that are using either exclusively sparse or dense algebra. Of course I would like to only implement a generic version of the algorithm what can deal with either of the two.
The first idea was to create a virtual vector class (code below for the concept, it wouldn't actually work this way):
class Vector {
virtual double dotproduct(Vector& other);
};
class SparseVector : public Vector {
double dotproduct(SparseVector& other) override;
};
class DenseVector : public Vector {
double dotproduct(DenseVector& other) override;
};
However that doesn't work because each of the Vector implementations can only work with other vectors of the same type. (That is, the implementation should not allow a dotproduct between a sparse and a dense vector).
Is there a good implementation strategy or design pattern that prevents me having to implement algorithms twice?
The question is not the same as this one, as I do not want to support dotproducts between a sparse and a dense vector.
I was thinking about using templates:
template<class T>
class algorithm {
}
but I don't know how to restrict T to be one of SparseVector/DenseVector, so the compiler knows that there exists a suitable dotproduct() function.
The thing you're looking for is called Visitor Pattern or Double Dispatch. You should be able to easily locate further infor online. However, the gist is basically this: With regular virtual functions or OOP, the invoked code depends on the type of one object. With the Visitor Pattern, you get to pick the code depending on two objects (hence also the name Double Dispatch), which is basically what you need.
I am writing a scientific code which needs to create 3-dimensional cells, defined by a set of faces, which are defined by a set of vertices.
These 3 classes (Cell, Face, Vertex) are derived respectively from some generic geometry classes (Polyhedron, Polygon, Point) which implement some geometric routines like Polygon::CalculateArea().
The Face class adds to the Polygon class with additional data and functions required for the science, like Face::Interpolate(). I don't want to make these member functions virtual in the base class (Polygon).
Now, the problem. I initialize a Cell with a vector of pointers to Face, which is handled by the base class Polyhedron constructor, which upcasts the Face* to Polygon*:
Polyhedron::Polyhedron( std::initializer_list<Polygon*> polygons );
Later, I want to access the Face* stored in a Cell so that I can call Face::Interpolate(), but it has been stored as a Polygon* and thus has no member function Polygon::Interpolate(). I can downcast it manually back to a Face* which works, but is not very clean. The user of the code has to do something like:
Face * temp_face = (Face*)cell->GetFaces()[0]; // Could use static_cast
temp_face->Interpolate();
which is not obvious.
I want the interface to be transparent, so that this just works:
cell->GetFaces()[0]->Interpolate();
I can think of two or three ways to achieve this. I'm looking for a better solution or feedback of which of these is recommended:
In Cell::GetFaces() which currently just inherits from Polyhedron::GetPolygons() I could create a wrapper that copies the std::vector<Polygon*> to a new vector std::vector<Face*>. This seems sloppy to me, not easy to maintain, inefficient and prone to errors.
Instead of storing std::vector<Polygon*> I could store std::vector<std::shared_ptr<Polygon>>. From what I understand, these smart pointers retain type-awareness so that they can call the right destructor, but they might just store a reference to the destructor depending on implementation. I don't want to use shared_ptr for performance purposes -- I know they're good and friendly, but I'm creating millions of these Polygons and its easy to destroy them in the right place. I can't use unique_ptr easily because of the copy-constructor used in std::initializer_list constructors.
Template the whole Polyhedron class, replacing every instance of Polygon* with F* and checking that F is a base of Polygon:
template<typename F = Polygon>
typename std::enable_if<std::is_base_of<Polygon, F>::value, void>::type
class Polyhedron
and then inheriting from a parent with a given typename:
class Cell : public Polyhedron<Face>
This seems like the best method to me, since it has the least boilerplate and nothing exposed to the user; but it still feels messy, especially in the "real" case where there might be multiple types that would all have to be specified:
class Cell: public Polyhedron<Face,Vertex,type3,type4,type5,...>
Is there a a better way? Perhaps a means of retaining type in the original vector (or some other container)?
If not, which of the above methods is the best practice and why?
Edit:
Here's an abstracted view of the problem. The problem occurs when trying to run sumOfSomethingSpecific(). In my actual problem, that function is inside a derived class Derived_B, which is designed to work with Derived_A, but for the sake of the problem, it makes no difference.
class Base_A
{
public:
Base_A();
~Base_A();
// I don't want virtual doSomethingSpecific() here.
};
class Derived_A
{
public:
using Base_A::Base_A;
double doSomethingSpecific();
};
// I could template this whole class
// template <typename T>
// where T replaces Base_A
class B
{
public:
// This can be initialized with:
// std::vector<Derived_A*>
// which is what I want to do, but we lose info about doSomethingSpecific()
// even if I write a separate constructor its still stored as
// std::vector<Base_A*>
B(std::vector<Base_A*> v) : v(v) {};
~B();
double sumOfSomethingSpecific()
{
double sum = 0;
for(auto&& A : v) {
// Can't do this, A is a pointer of type Base_A*, but this is the abstraction that I want to achieve
sum += A->doSomethingSpecific();
// Could do this, but its ugly and error-prone
Derived_A* tempA = (Derived_A*)A;
sum += tempA->doSomethingSpecific();
}
return sum;
}
protected:
std::vector<Base_A*> v;
};
First most of issues you're facing here are not about programming, are about design.
... class with additional data and functions required for the science, like Face::Interpolate(). I don't want to make these member functions virtual in the base class (Polygon). ...
Well, don't do that, but then you have to realize that you're adding complexity to the code you need to implement such design desicion.
However, if every polygon can be "interpolated" then you should have a virtual function (or better yet a pure virtual function) in your Polygon class.
Said that, with the code as it is, in order to add transparency to the API you declare you get_* functions as:
void GetFaces(std::vector<Face *> &faces);
that way is clear for the user that he/she has to provide a reference to a vector of faces to get the result. Lets see how this change your code:
// Face * temp_face = (Face*)cell->GetFaces()[0]; // Could use static_cast
std::vector<Face *> temp_faces;
cell->GetFaces(temp_faces);
//temp_face->Interpolate();
temp_faces[0]->Interpolate();
This way the down-cast is performed implicitly.
About your question: Is there a a better way? Yes, redesign your classes.
About your example:
I will ask you to think a moment about this:
struct Base {};
struct Derived_A: Base { double doSomethingSpecific(); };
struct Derived_B: Base { double doSomethingSpecific(); };
int main()
{
std::vector<Base*> base_v = {/*suppose initialization here*/};
base_v[0]->doSomethingSpecific(); // Which function must be called here?
// Derived_A::doSomethingSpecific or
// Derived_B::doSomethingSpecific.
}
At some point you will have to tell wich type you want call the function on.
The level of abstraction you want, does not exists in C++. The compiler needs to know the type of an object in order to perform (compile) a call to one of its member functions.
Another approach you can try (I still recommend to redesign):
If you have the need of manipulating several distinct types in a uniform manner. Perhaps you want to take a look at Boot.Variant library.
I struggled with a similar problem in one of my projects. The solution I used was to give ownership of the actual objects to the most-derived class, give the base class a copy of the objects, and use a virtual function to keep the copy up-to-date as objects are added/removed:
class Polyhedron {
protected:
bool _polygons_valid = false;
std::vector<Polygon*> _polygons;
virtual void RebuildPolygons() = 0;
public:
std::vector<Polygon*>& GetPolygons()
{
if (!_polygons_valid) {
RebuildPolygons();
_polygons_valid = true;
}
return _polygons;
}
/*Call 'GetPolygons()' whenever you need access to the list of polygons in base class*/
};
class Cell: public Polyhedron {
private:
std::vector<Face*> _faces; //Remember to set _polygons_valid = false when modifying the _faces vector.
public:
Cell(std::initializer_list<Face*> faces):
_faces(faces) {}
//Reimplement RebuildPolygons()
void RebuildPolygons() override
{
_polygons.clear();
for (Face* face : _faces)
_polygons.push_back(face);
}
};
This design has the benefits of clear ownership (most-derived class is owner), and that copying and upcasting the vector of object pointers is done only when needed. The downside is that you have two copies of essentially the same thing; a vector of pointers to objects. The design is very flexible too, since any class derived from Polyhedron only has to implement the RebuildPolygons() function, using a vector of any type derived from Polygon.
Assume that I have a strategy interface named BinaryClassifier that can take a Sample and return a double representing the probability of a Sample object of belonging to the positive class:
struct BinaryClassifier {
virtual ~BinaryClassifier(){}
virtual double classify(std::shared_ptr<Sample> sample) const = 0;
};
We may have several implementations of BinaryClassifier, e.g., LogisticRegressionBinaryClassifier.
The Sample is in turn an interface exposing only two methods:
struct Sample {
virtual ~Sample() {}
InputFeatures const& get_input_features() const = 0;
double get_label() const = 0;
};
Apart from these two methods, concrete implementations of Sample expose quite different interfaces (i.e., they are unrelated), the only aspect they have in common is that they can be classified by a binary classifier.
So far so good.
Problems arise when we decide to introduce the BinaryClassifier::train method:
struct BinaryClassifier {
virtual ~BinaryClassifier(){}
virtual double classify(std::shared_ptr<Sample> sample) const = 0;
virtual void train(std::vector<std::shared_ptr<Sample>> samples) = 0;
};
At this point, the following won't work:
std::vector<std::shared_ptr<ConcreteSample>> concreteSamples = ...;
concreteBinaryClassifier.train(concreteSamples);
that's because std::vector<std::shared_ptr<ConcreteSample>> and std::vector<std::shared_ptr<Sample>> are two unrelated types.
The C++-ish solution would be to rely on templates:
template<class SampleType>
virtual void train(std::vector<std::shared_ptr<SampleType>> samples) = 0; // non-working code, template method cannot be virtual
But template methods cannot be virtual. Still, I would like BinaryClassifier to be a Strategy interface as many possible BinaryClassifier implementations may exist. At this point, even though the design seems quite resonable, I'm stuck at a dead end.
EDIT: Moreover, it may happen for a given BinaryClassifier object to be trained with a vector of ConcreteSampleA, while classifying object of type ConcreteSampleB
Which is the right way of modeling this situation in the most C++-ish way?
You can make your BinaryClassifier a template class
template<SampleType> class BinaryClassifier
{
virtual void train(std::vector<std::shared_ptr<SampleType>> samples) = 0;
}
You can't train a BinaryClassifier on ConcreteSampleA and then use it to classify any random ConcreteSampleB. Therefore, the sample type is an inherent part of BinaryClassifier. Nullref's answer is reasonable there: make the sample type a template parameter.
As you discovered, that means there's no longer a need for a Sample interface. Good. std::vector<int> doesn't require that int is derived from some Element interface either.
While you're getting rid of stuff, InputFeatures looks suspect too. I'd just say that get_input_features must return some std::tuple whose member types all have std::less defined. Since it's no longer virtual, you don't care that different sample types return different tuples. And I definitely wouldn't hardcode that get_label must return a double. That's a weird label type anyway.
Now you said that it may make sense to train with a sample type A and then classify sample type B. That's where you get to a refinement: it seems that the actual compatibility requirement is that they return the same tuple. Therefore, a better solution that Nullref's is to templatize BinaryClassifier on the tuple type returned by get_input_sample.
[edit]
Furthermore, classify doesn't need to take co-ownership. Pass a Sample const&. train() really should take just an iterator pair. C++ convention is to pass a group of objects as a range.
What is a common practice for the storage of a list of base class pointers each of which can describe a polymorphic derived class?
To elaborate and in the interest of a simple example lets assume that I have a set of classes with the following goals:
An abstract base class whose purpose is to enforce a common functionality on its derived classes.
A set of derived classes which: can perform a common functionality, are inherently copyable (this is important), and are serializable.
Now alongside this required functionality I want to address the following key points:
I want the use of this system to be safe; I don't want a user to have undefined errors when he/she erroneously casts a base class pointer to the wrong derived type.
Additionally I want as much as possible the work for copying/serializing this list to be taken care of automatically. The reason for this is, as a new derived type is added I don't want to have to search through many source files and make sure everything will be compatible.
The following code demonstrates a simple case of this, and my proposed (again I am looking for a common well thought out method of doing this, mine may not be so good) solution.
class Shape {
public:
virtual void draw() const = 0;
virtual void serialize();
protected:
int shapeType;
};
class Square : public Shape
{
public:
void draw const; // draw code here.
void serialize(); // serialization here.
private:
// square member variables.
};
class Circle : public Shape
{
public:
void draw const; // draw code here.
void serialize(); // serialization here.
private:
// circle member variables.
};
// The proposed solution: rather than store list<shape*>, store a generic shape type which
// takes care of copying, saving, loading and throws errors when erroneous casting is done.
class GenericShape
{
public:
GenericShape( const Square& shape );
GenericShape( const Circle& shape );
~GenericShape();
operator const Square& (); // Throw error here if a circle tries to get a square!
operator const Circle& (); // Throw error here if a square tries to get a circle!
private:
Shape* copyShape( const Shape* otherShape );
Shape* m_pShape; // The internally stored pointer to a base type.
};
The above code is certainly missing some items, firstly the base class would have a single constructor requiring the type, the derived classes would internally call this during their construction. Additionally in the GenericShape class, copy/assignment constructor/operator would be present.
Sorry for the long post, trying to explain my intents fully. On that note, and to re-iterate: above is my solution, but this likely has some serious flaws and I would be happy to hear about them, and the other solutions out there!
Thank you
What is the problem of a std::list< shape* > (or a std::list< boost::shared_ptr > thereof)?
That would be the idiomatic way of implementing a list of shapes with polymorphic behavior.
I want the use of this system to be safe; I don't want a user to have undefined errors when he/she erroneously casts a base class pointer to the wrong derived type.
Users should not downcast, but rather use the polymorphism and the base (shape) operations provided. Consider why they would be interested in downcasting, if you find a reason to do so, go back to drawing board and redesign so that your base provides all needed operations.
Then if the user wants to downcast, they should use dynamic_cast, and they will get the same behavior you are trying to provide in your wrapper (either a null pointer if downcasting pointers or a std::bad_cast exception for reference downcasting).
Your solution adds a level of indirection and (with the provided interface) require the user to try guessing the type of shape before use. You offer two conversion operators to each of the derived classes, but the user must call them before trying to use the methods (that are no longer polymorphic).
Additionally I want as much as possible the work for copying/serializing this list to be taken care of automatically. The reason for this is, as a new derived type is added I don't want to have to search through many source files and make sure everything will be compatible.
Without dealing with deserialization (I will come back later), your solution, as compared to storing (smart) pointers in the list, requires revisiting the adapter to add new code for each and every other class that is added to the hierarchy.
Now the deserialization problem.
The proposed solution is using a plain std::list< boost::shared_ptr >, once you have the list built, drawing and serialization can be performed right out of the box:
class shape
{
public:
virtual void draw() = 0;
virtual void serialize( std::ostream& s ) = 0;
};
typedef std::list< boost::shared_ptr<shape> > shape_list;
void drawall( shape_list const & l )
{
std::for_each( l.begin(), l.end(), boost::bind( &shape::draw, _1 ));
}
void serialize( std::ostream& s, shape_list const & l )
{
std::for_each( l.begin(), l.end(), boost::bind( &shape::serialize, _1, s ) );
}
Where I have used boost::bind to reduce code bloat instead of iterating manually. The problem is that you cannot virtualize construction as before the object has been constructed you cannot know what type it actually is. After the problem of deserializing one element of a known hierarchy is solved, deserializing the list is trivial.
Solutions to this problem are never as clean and simple as the code above.
I will assume that you have defined unique shape type values for all shapes, and that your serialization starts by printing out that id. That is, the first element of serialization is the type id.
const int CIRCLE = ...;
class circle : public shape
{
// ...
public:
static circle* deserialize( std::istream & );
};
shape* shape_deserialize( std::istream & input )
{
int type;
input >> type;
switch ( type ) {
case CIRCLE:
return circle::deserialize( input );
break;
//...
default:
// manage error: unrecognized type
};
}
You can further alleviate the need to work on the deserializer function if you convert it into an abstract factory where upon creation of a new class the class itself registers it's deserialization method.
typedef shape* (*deserialization_method)( std::istream& );
typedef std::map< int, deserialization_method > deserializer_map;
class shape_deserializator
{
public:
void register_deserializator( int shape_type, deserialization_method method );
shape* deserialize( std::istream& );
private:
deserializer_map deserializers_;
};
shape* shape_deserializator::deserialize( std::istream & input )
{
int shape_type;
input >> shape_type;
deserializer_map::const_iterator s = deserializers_.find( shape_type );
if ( s == deserializers_.end() ) {
// input error: don't know how to deserialize the class
}
return *(s->second)( input ); // call the deserializer method
}
In real life, I would have used boost::function<> instead of the function pointers, making the code cleaner and clearer, but adding yet another dependency to the example code. This solution requires that during initialization (or at least before trying to deserialize) all classes register their respective methods in the shape_deserializator object.
You could avoid lots of repetition in GenericShape by using templates (for the constructors and converters), but the key bit that's missing is having it inherit from Shape and implement its virtuals -- without it it's unusable, with it it's a pretty normal variant on envelope/implementation idioms.
You may want to use auto_ptr (or somewhat-smarter pointers) rather than a bare pointer to Shape, too;-).
I would propose boost::shared_pointer<Shape> in an STL container. Then use dynamic_cast to downcast guarantee type correctness. If you want to provide helper functions to toss exceptions instead of returning NULL, then follow Alex's suggestion and define a template helper function like:
template <typename T, typename U>
T* downcast_to(U *inPtr) {
T* outPtr = dynamic_cast<T*>(inPtr);
if (outPtr == NULL) {
throw std::bad_cast("inappropriate cast");
}
return outPtr;
}
and use it like:
void some_function(Shape *shp) {
Circle *circ = downcast_to<Circle>(shp);
// ...
}
Using a separate class like GenericShape is just too strongly coupled with every class that descends from Shape. I wonder if this would be considered a code smell or not...
I want the use of this system to be
safe; I don't want a user to have
undefined errors when he/she
erroneously casts a base class pointer
to the wrong derived type.
Why would you get undefined errors? The behavior of dynamic_cast is perfectly well-defined and catches the error if you cast a base class pointer to the wrong derived type. This really seems like reinventing the wheel.
Additionally I want as much as
possible the work for
copying/serializing this list to be
taken care of automatically. The
reason for this is, as a new derived
type is added I don't want to have to
search through many source files and
make sure everything will be
compatible.
I'm not sure what the problem is here. If all the derived classes are serializable and copyable, isn't that good enough? What more do you need?
I'm also not sure what to make of the first two requirements.
What do you mean, the ABC should "enforce a common functionality"? And what is the point in having derived classes, if their role is only to perform that same common functionality, be copyable and serializable?
Why not just make one non-abstract class serializable and copyable then?
I'm probably missing something vital here, but I don't really think you've explained what it is you're trying to achieve.