This question is related to this other one. I'm trying to implement the virtual method
approach suggested in one of the answers.
I have an abstract base class representing an object described by a Level-Set function
class LevelSetObject
{
public:
virtual double SDF(double x, double y, double z) const = 0;
/** @brief Union of two LevelSetObjects */
CompositeLevelSetObject operator+(LevelSetObject& other);
virtual ~LevelSetObject() = default;
};
The first implementation I had with std::function
was working more or less as expected. The difference now is that I return a CompositeLevelSetObject
when adding up two generic LevelSetObjects
CompositeLevelSetObject LevelSetObject::operator+(LevelSetObject& other) {
CompositeFun* fun = [] (double sdf_value1, double sdf_value2) {
return std::min(sdf_value1, sdf_value2);
};
auto first = std::unique_ptr<LevelSetObject>(new LevelSetObject(*this));
auto second = std::unique_ptr<LevelSetObject>(new LevelSetObject(other));
return { std::move(first), std::move(second), fun };
}
A CompositeLevelSetObject
, at least in my mind :), is an object that takes as input pointers the two objects it's composed by, plus a function pointer that computes the resulting distance function (Primitive combinations of LevelSetObjects
are encoded by operations between the two single distance function of the objects ref ("Primitive Combinations") )
/** @brief This is the function that is called on the two single SDF function of the objects */
using CompositeFun = double (double, double);
/**
* @brief A class representing a composite LevelSetObject coming out of operation between two other LevelSetObjects
*
* This class stores pointers to the two LevelSetObject and executes a given function on both of them
*/
class CompositeLevelSetObject : public LevelSetObject
{
using LevelSetObjectPtr = std::unique_ptr<LevelSetObject>;
private:
LevelSetObjectPtr m_first;
LevelSetObjectPtr m_second;
CompositeFun* m_fun;
public:
CompositeLevelSetObject() = default;
CompositeLevelSetObject(LevelSetObjectPtr first, LevelSetObjectPtr second, CompositeFun* m_fun);
CompositeLevelSetObject(CompositeLevelSetObject&&);
double SDF(double x, double y, double z) const override;
// :: Operators ::
CompositeLevelSetObject& operator=(CompositeLevelSetObject&&);
};
}
double CompositeLevelSetObject::SDF(double x, double y, double z) const {
return m_fun(m_first->SDF(x, y, z), m_second->SDF(x, y, z));
}
This clearly does not work, because in the LevelSetObject::operator+
, I'm creating the pointers instantiating an abstract class.
allocating an object of abstract class type 'hgve::LevelSetObject'
auto first = std::unique_ptr<LevelSetObject>(new LevelSetObject(*this));
So I tried to switch the LevelSetObject::SDF
method to be non-pure, returning 0
. But clearly doesn't work because then everything returns 0.
I'm missing something in my conceptual organisation of the data structure, probably due to my dynamic languages background, but I can't see it. Any help is appreciated.
An example of usage:
// Vector containing a list of LevelSetSpheres
static std::vector<LevelSetSphere> elements = {
LevelSetSphere{radius, {radius, 0.25, 0.25}},
LevelSetSphere{radius, {radius + interDistance, 0.25, 0.25}},
LevelSetSphere{radius, {radius + 2*interDistance, 0.25, 0.25}}
};
int main(int argc, char* argv[]) {
// Sum first two elements
CompositeLevelSetObject soot = elements[0] + elements[1];
for(auto el = std::next(elements.begin(), 2); el != elements.end(); ++el) {
// Combine elements
soot = soot + *el;
}
}
LevelSetSphere
is a derived class from LevelSetObject
.
class LevelSetSphere : public LevelSetObject
{
private:
double m_R; /**< The radius of the sphere */
SimpleVector m_C; /**< The center of the sphere */
public:
/** @brief Constructor
*
* @param radius The radius of the sphere
*/
LevelSetSphere(double radius, SimpleVector center);
double SDF(double x, double y, double z) const override;
};
When you do new LevelSetObject
, this creates a new object whose type is LevelSetObject. Seems obvious, but it needs to be pointed out.
You don't want a new LevelSetObject
, you want a new LevelSetSphere
(if the input object is a sphere).
You can take a LevelSetObject
and transfer ownership to a unique_ptr
without copying, but you have to be careful with the ownership. If you want to do this, it's better to use a unique_ptr
from the start (option 3). You can only transfer ownership if the object was created with new
. Local variables, global variables, and elements of vectors (like in your example), among other things, can't be transferred to unique_ptr
. Only objects created with new
.
Here are three possible solutions:
Add a virtual std::unique_ptr<LevelSetObject> copy() const = 0;
method to LevelSetObject
. (Implement this in every derived class, so it makes a copy)
Make the CompositeLevelSet object point to the original LevelSetObject
s instead of copying them. This means you can't use the composite object after the original objects are destroyed. It also means the intermediate compositions need to be saved somewhere. Not a great option.
Pass ownership into operator+
so that it doesn't need to copy the objects. For example, define a unique_ptr<CompositeLevelSetObject> operator+(unique_ptr<LevelSetObject> left, unique_ptr<LevelSetObject> right)
. (If this confuses you, it can be a function instead of an operator)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.