I'm working on modelling some hardware in SystemC (although that's not relevant to the question). The goal is to be able to declare a bunch of registers in a block (class) which are used directly in the class implementation. The external software/firmware will access the registers through a register map to decode based on address. So the HW block as part of its constructor will initialize the register map (not shown below). The issue arises from the fact that some registers need to trigger an action. To do this is seems appropriate to have method in the HW class that is called if the register is written.
The simplified class hierarchy is as follows
class regBase {
public:
regBase(uint64_t _address, std::string _name) :
m_address(_address),
m_name(_name) { }
regBase() {};
void setAddress(uint64_t _address) { m_address = _address;}
virtual void write(uint32_t newval) = 0; //virtual methods to be overridden in template
virtual uint32_t read(void) = 0;
virtual int size(void) = 0;
private:
uint64_t m_address;
std::string m_name;
};
template <class REG>
class hwRegister : public regBase
{
public:
uint32_t read(void) override
{
return m_val.value();
}
void write(uint32_t newval) override
{
m_val.setValue(newval);
}
int size(void) override
{
return (m_val.size());
}
private:
REG m_val;
};
typedef std::function<void(uint64_t, uint32_t)> writeCallback_t;
struct reg_entry {
reg_entry(int _size, regBase *_reg) :
m_reg(_reg), m_size(_size) {}
reg_entry() {}
regBase *m_reg;
int m_size;
writeCallback_t m_callback;
};
typedef boost::ptr_map<int, std::shared_ptr<reg_entry> > reg_map_t;
class RegMap {
public:
void write(uint64_t address, uint32_t val) {
auto it = m_register_map.find(address);
if (it==m_register_map.end())
{
BOOST_ASSERT_MSG(false, "Invalid address");
}
auto entry = *it->second;
entry->m_reg->write(val);
if (entry->m_callback)
entry->m_callback(address, val);
};
void setCallback(uint64_t address, writeCallback_t newcallback)
{
auto it = m_register_map.find(address);
if (it==m_register_map.end())
{
BOOST_ASSERT_MSG(false, "Invalid address");
}
(*it->second)->m_callback = newcallback;
};
void addReg(uint64_t address, regBase *reg) {
auto entry = std::make_shared<reg_entry>(reg->size(), reg);
entry->m_callback = nullptr;
entry->m_reg = reg;
m_register_map[address] = entry;
}
private:
reg_map_t m_register_map;
};
I want to in the implementation of HW block, add myReg to myMap and be able to add a callback if necessary for a given register. The issue is around the callback. Does the declaration of the callback look right? Do I need to use std::bind and placeholders?
Ok, updated with compiling code.
[this](uint64_t address, uint32_t val) { myCallback(address, val); } worked
struct my_reg_st {
uint64_t data;
uint64_t size(void) { return 8; };
void setValue(uint32_t val) { data = val; };
uint64_t value(void) {return data; };
};
class test {
public:
test() {
myMap.addReg(0, &myReg);
// use lambda function for callback. Note that use of intermediate f is only for readability
writeCallback_t f = [this](uint64_t address, uint32_t val) { myRegCallback(address, val); };
myMap.setCallback(0, f);
}
void myRegCallback(uint64_t address, uint32_t value);
void write(uint64_t address, uint32_t val) { myMap.write(address, val); };
private:
hwRegister<my_reg_st> myReg;
RegMap myMap;
};
I have generated multiple classes from the protobuf files with the protcompiler. In these classes there is the redefinition of the assignment operator and when I try to make an assagnment the program returns segfault. In particular the error that returns is 0x000000000060be60 in PtrTag (this=0x7f1808001a28) at /usr/include/google/protobuf/metadata_lite.h:136 136 return reinterpret_cast<intptr_t>(ptr_) & kPtrTagMask. This function is a library function and I don't know why returns error. There is some library that I have to link in compilation time in addition to the libprotobuf or some object files?
A sample of class generated by protoc is:
class LocalizationEstimate : public ::google::protobuf::Message /* ##protoc_insertion_point(class_definition:apollo.localization.LocalizationEstimate) */ {
public:
LocalizationEstimate();
virtual ~LocalizationEstimate();
LocalizationEstimate(const LocalizationEstimate& from);
inline LocalizationEstimate& operator=(const LocalizationEstimate& from) {
CopyFrom(from);
return *this;
}
inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
return _internal_metadata_.unknown_fields();
}
inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
return _internal_metadata_.mutable_unknown_fields();
}
static const ::google::protobuf::Descriptor* descriptor();
static const LocalizationEstimate& default_instance();
static inline const LocalizationEstimate* internal_default_instance() {
return reinterpret_cast<const LocalizationEstimate*>(
&_LocalizationEstimate_default_instance_);
}
static PROTOBUF_CONSTEXPR int const kIndexInFileMessages =
1;
void Swap(LocalizationEstimate* other);
// implements Message ----------------------------------------------
inline LocalizationEstimate* New() const PROTOBUF_FINAL { return New(NULL); }
LocalizationEstimate* New(::google::protobuf::Arena* arena) const PROTOBUF_FINAL;
void CopyFrom(const ::google::protobuf::Message& from) PROTOBUF_FINAL;
void MergeFrom(const ::google::protobuf::Message& from) PROTOBUF_FINAL;
void CopyFrom(const LocalizationEstimate& from);
void MergeFrom(const LocalizationEstimate& from);
void Clear() PROTOBUF_FINAL;
bool IsInitialized() const PROTOBUF_FINAL;
size_t ByteSizeLong() const PROTOBUF_FINAL;
bool MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input) PROTOBUF_FINAL;
void SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const PROTOBUF_FINAL;
::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
bool deterministic, ::google::protobuf::uint8* target) const PROTOBUF_FINAL;
int GetCachedSize() const PROTOBUF_FINAL { return _cached_size_; }
private:
void SharedCtor();
void SharedDtor();
void SetCachedSize(int size) const PROTOBUF_FINAL;
void InternalSwap(LocalizationEstimate* other);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return NULL;
}
inline void* MaybeArenaPtr() const {
return NULL;
}
public:
::google::protobuf::Metadata GetMetadata() const PROTOBUF_FINAL;
// nested types ----------------------------------------------------
// accessors -------------------------------------------------------
// repeated .apollo.common.TrajectoryPoint trajectory_point = 5;
int trajectory_point_size() const;
void clear_trajectory_point();
static const int kTrajectoryPointFieldNumber = 5;
const ::apollo::common::TrajectoryPoint& trajectory_point(int index) const;
::apollo::common::TrajectoryPoint* mutable_trajectory_point(int index);
::apollo::common::TrajectoryPoint* add_trajectory_point();
::google::protobuf::RepeatedPtrField< ::apollo::common::TrajectoryPoint >*
mutable_trajectory_point();
const ::google::protobuf::RepeatedPtrField< ::apollo::common::TrajectoryPoint >&
trajectory_point() const;
// optional .apollo.common.Header header = 1;
bool has_header() const;
void clear_header();
static const int kHeaderFieldNumber = 1;
const ::apollo::common::Header& header() const;
::apollo::common::Header* mutable_header();
::apollo::common::Header* release_header();
void set_allocated_header(::apollo::common::Header* header);
// optional .apollo.localization.Pose pose = 2;
bool has_pose() const;
void clear_pose();
static const int kPoseFieldNumber = 2;
const ::apollo::localization::Pose& pose() const;
::apollo::localization::Pose* mutable_pose();
::apollo::localization::Pose* release_pose();
void set_allocated_pose(::apollo::localization::Pose* pose);
// optional .apollo.localization.Uncertainty uncertainty = 3;
bool has_uncertainty() const;
void clear_uncertainty();
static const int kUncertaintyFieldNumber = 3;
const ::apollo::localization::Uncertainty& uncertainty() const;
::apollo::localization::Uncertainty* mutable_uncertainty();
::apollo::localization::Uncertainty* release_uncertainty();
void set_allocated_uncertainty(::apollo::localization::Uncertainty* uncertainty);
// optional .apollo.localization.MsfStatus msf_status = 6;
bool has_msf_status() const;
void clear_msf_status();
static const int kMsfStatusFieldNumber = 6;
const ::apollo::localization::MsfStatus& msf_status() const;
::apollo::localization::MsfStatus* mutable_msf_status();
::apollo::localization::MsfStatus* release_msf_status();
void set_allocated_msf_status(::apollo::localization::MsfStatus* msf_status);
// optional .apollo.localization.MsfSensorMsgStatus sensor_status = 7;
bool has_sensor_status() const;
void clear_sensor_status();
static const int kSensorStatusFieldNumber = 7;
const ::apollo::localization::MsfSensorMsgStatus& sensor_status() const;
::apollo::localization::MsfSensorMsgStatus* mutable_sensor_status();
::apollo::localization::MsfSensorMsgStatus* release_sensor_status();
void set_allocated_sensor_status(::apollo::localization::MsfSensorMsgStatus* sensor_status);
// optional double measurement_time = 4;
bool has_measurement_time() const;
void clear_measurement_time();
static const int kMeasurementTimeFieldNumber = 4;
double measurement_time() const;
void set_measurement_time(double value);
// ##protoc_insertion_point(class_scope:apollo.localization.LocalizationEstimate)
private:
void set_has_header();
void clear_has_header();
void set_has_pose();
void clear_has_pose();
void set_has_uncertainty();
void clear_has_uncertainty();
void set_has_measurement_time();
void clear_has_measurement_time();
void set_has_msf_status();
void clear_has_msf_status();
void set_has_sensor_status();
void clear_has_sensor_status();
::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;
::google::protobuf::internal::HasBits<1> _has_bits_;
mutable int _cached_size_;
::google::protobuf::RepeatedPtrField< ::apollo::common::TrajectoryPoint > trajectory_point_;
::apollo::common::Header* header_;
::apollo::localization::Pose* pose_;
::apollo::localization::Uncertainty* uncertainty_;
::apollo::localization::MsfStatus* msf_status_;
::apollo::localization::MsfSensorMsgStatus* sensor_status_;
double measurement_time_;
friend struct protobuf_modules_2flocalization_2fproto_2flocalization_2eproto::TableStruct;
};
Follow the code of the function called in the redefinition of the assignment operator:
void LocalizationEstimate::CopyFrom(const LocalizationEstimate& from) {
// ##protoc_insertion_point(class_specific_copy_from_start:apollo.localization.LocalizationEstimate)
if (&from == this) return;
Clear();
MergeFrom(from);
}
void LocalizationEstimate::MergeFrom(const LocalizationEstimate& from) {
// ##protoc_insertion_point(class_specific_merge_from_start:apollo.localization.LocalizationEstimate)
GOOGLE_DCHECK_NE(&from, this);
_internal_metadata_.MergeFrom(from._internal_metadata_);
::google::protobuf::uint32 cached_has_bits = 0;
(void) cached_has_bits;
trajectory_point_.MergeFrom(from.trajectory_point_);
cached_has_bits = from._has_bits_[0];
if (cached_has_bits & 63u) {
if (cached_has_bits & 0x00000001u) {
mutable_header()->::apollo::common::Header::MergeFrom(from.header());
}
if (cached_has_bits & 0x00000002u) {
mutable_pose()->::apollo::localization::Pose::MergeFrom(from.pose());
}
if (cached_has_bits & 0x00000004u) {
mutable_uncertainty()->::apollo::localization::Uncertainty::MergeFrom(from.uncertainty());
}
if (cached_has_bits & 0x00000008u) {
mutable_msf_status()->::apollo::localization::MsfStatus::MergeFrom(from.msf_status());
}
if (cached_has_bits & 0x00000010u) {
mutable_sensor_status()->::apollo::localization::MsfSensorMsgStatus::MergeFrom(from.sensor_status());
}
if (cached_has_bits & 0x00000020u) {
measurement_time_ = from.measurement_time_;
}
_has_bits_[0] |= cached_has_bits;
}
}
In my main function I made somethinghs like:
LocalizationEstimate l = localization;
where localization is a shared variable with an other process. But this assignment return segfault.
I was wondering if there is a way to register variable inside class, in this case simply print variable name.
#include <iostream>
void register_variable(const char* name) { std::cout <<'\n' << name << '\n'; }
#define VARIABLE(connector) \
connector; \
register_variable(#connector);
class Test {
public:
VARIABLE(int b) // does not compile
};
int main()
{
VARIABLE(int a) // works
}
Following code produce this compilation error:
main.cpp:11:19: error: expected identifier before string constant
VARIABLE(int b) // does not compile
^
main.cpp:7:24: note: in definition of macro ‘VARIABLE’
register_variable(#connector);
^~~~~~~~~
main.cpp:11:19: error: expected ‘,’ or ‘...’ before string constant
VARIABLE(int b) // does not compile
^
main.cpp:7:24: note: in definition of macro ‘VARIABLE’
register_variable(#connector);
^~~~~~~~~
main.cpp:7:33: error: ISO C++ forbids declaration of ‘register_variable’ with no type [-fpermissive]
register_variable(#connector);
^
main.cpp:11:5: note: in expansion of macro ‘VARIABLE’
VARIABLE(int b) // does not compile
^~~~~~~~
I understand that calling function inside class definition is forbidden but maybe there is trick how to do it.
Your approach does not work because you can't call a function in a class declaration. Your macro expands to:
class Test {
public:
int b;
register_variable("int b"); // This isn't allowed here, it should be in a constructor
};
There are few solutions to do what you intend, but it require more code.
The ideal solution would be to wait for C++21 or later when true reflection will be accepted in the standard. In that case you'll have to query the type via template code to get all members name and then call something like cout << meta<Test>::get_member<0>(testInstance)
Unfortunately, until this is set up in stone, you only have to rely on some other tricks, like one of the two I'm exposing below:
Trick 1
Split declaration and definition in 2 macros instead of one, like this:
#define DeclareVariable(X) X
#define DefineVariable(X) register_variable(#X)
// Used like
struct Test {
DeclareVariable(int b);
Test() {
DefineVariable(b);
}
};
Obviously, you might want more than those very simple useless macro, like, for example, linking the variable name with a pointer to the variable via a hash table (it's done in DefineVariable macro). It's quite easy to do that, I won't show it.
The downside of this method is that, maintenance is painful. The type of the variable must be handled to where it's being used (meaning if you want to declare a float X, a MyStruct Y ...), you'll need a register_variable that's able to deal with those types as well.
Trick 2 (template usage)
I'm supposing you want to implement some kind of reflection to your class Test. How do you do that generically so it does not break on first slight modification ?
First, let's explain how it works:
I'm using the fact that member name are all different
It's possible to specialize template based on function address
Introspection is done via a table of metadata about your members
This table is static to the class so it's stored as a static struct of pointers to the templated functions (typically, it's called a virtual table)
Here's how I would do it (boiler plate):
struct MemberBase
{
void * ptr;
const char * type;
const char * name;
MemberBase(void * ptr, const char * type, const char * name) : ptr(ptr), type(type), name(name) {}
virtual ~MemberBase();
virtual void set_virt(const void * u);
virtual void get_virt(void * u);
};
template <typename U>
struct Member : public MemberBase
{
Member() : MemberBase(0, typename(U), 0) {}
Member(U & u, const char * type, const char * name) : MemberBase(&u, type, name) {}
void set(const U & u) { *static_cast<U*>(ptr) = u; }
void get(U & u) const { if (ptr) u = *static_cast<U*>(ptr); }
void set_virt(const void * u) { if (ptr) set(static_cast<const U*>(u)); }
void get_virt(void * u) { get(static_cast<U*>(u)); }
};
template <typename U>
struct Introspect
{
typedef U ClassType;
struct VirtualTable
{
const char * (*get_name)();
const char * (*get_type)();
MemberBase * (*get_ref)(U & object);
void (*get)(U & object, void * value);
void (*set)(U & object, const void * value);
};
template <typename Member, Member & (T::*Method)(), const char * (*Name)(), const char *(*Type)()>
struct VirtualTableImpl
{
static const char * get_name() { return (*Name)(); }
static const char * get_type() { return (*Type)(); }
static MemberBase* get_ref(T & object) { return &(object.*Method)(); }
static void get(T & object, void * _args) { (object.*Method)().get_virt(_args); }
static void set(T & object, const void * _args) { (object.*Method)().set_virt(_args); }
};
template <typename Member, Member & (T::*Method)(), const char * (*Name)(), const char *(*Type)()>
static VirtualTable & get_member_table()
{
static VirtualTable table =
{
&VirtualTableImpl<Member, Method, Name, Type>::get_name,
&VirtualTableImpl<Member, Method, Name, Type>::get_type,
&VirtualTableImpl<Member, Method, Name, Type>::get_reference,
&VirtualTableImpl<Member, Method, Name, Type>::get,
&VirtualTableImpl<Member, Method, Name, Type>::set,
};
return table;
}
private:
// We only need to register once
static bool & registered_already() { static bool reg = false; return reg; }
public:
typedef std::vector<VirtualTable> MembersMeta;
// Get the members for this type
static MembersMeta & get_members() { static MembersMeta mem; return mem; }
// Register a member
template <typename Member, Member & (T::*Method)(), const char * (*Name)(), const char * (*Type)()>
static void register_member()
{
if (registered_already()) return;
MembersMeta & mem = get_members();
if (mem.size() && strcmp(mem[0]->getName(), Name()) == 0)
registered_already() = true;
else mem.push_back(&get_member_table<Member, Method, Name, Type>());
}
static size_t find_member(const char * name)
{
MembersMeta & mem = get_members();
if (!name) return mem.size();
for (size_t i = 0; i < mem.size(); i++)
if (mem[i]->get_name() && strcmp(mem[i]->get_name(), name) == 0) return i;
return mem.size();
}
virtual MemberBase * get_member(const size_t i)
{
T& t = static_cast<T&>(*this);
MemberBase * h = get_members()[i]->get_ref(t);
return h;
}
};
#define CONCAT(X, Y, Z) X ## Y ## Z
#define DeclareMember(Type, Name) Type Name; \
Member<Type> prop_##Name; \
inline Member<Type> & get_prop_##Name () { return prop_##Name; } \
inline static const char * CONCAT(get_,Name,_name)() { return #Name; } \
inline static const char * CONCAT(get_,Name,_type)() { return #Type; } \
RegisterMember<Member<PropertyType>, ClassType, &ClassType::get_prop_##Name , &CONCAT(get_,Name,_name), &CONCAT(get_,Name,_type)> _regProp_##Name ; \
#define DefineMember(X) prop_#X(X, CONCAT(get_,X,_type)(), CONCAT(get_,X,_name)())
template <typename Mem, typename Obj, Mem & (Obj::*Method)(), const char * (*Name)(), const char * (*Type)()>
struct RegisterMember
{
RegisterMember()
{
Introspect<Obj>::template register_member<Prop, Method, Name, Type>();
}
};
Then usage (hopefully, it's much easier to use:
struct Test : public Introspect<Test>
{
DeclareMember(int, x);
Test(int a) : x(a), DefineMember(x)
{
}
};
int main(...) {
Test t(2);
// Usual usage: t.x is an true int member
t.x = 42;
// Indirect usage: check if a member exist
size_t i = t.find_member("x");
if (i != t.get_members().size()) {
// Capture t member
MemberBase * member = t.get_member(i);
// If you know the type (else you can try a dynamic_cast here)
Member<int> * mem_int = static_cast<Member<int>*>(member);
int ret = 0;
mem_int->get(ret);
std::cout << member->type << " " << member->name << ": "<< ret << std::endl; // int x: 42
// If you don't know the type, it's harder and unsafe
member->get_virt(&ret);
std::cout << "generic: " << ret << std::endl; // generic: 42
// Example without a dynamic_cast
if (strcmp(member->type, "int") == 0) {
Member<int> * mem_int = static_cast<Member<int>*>(member);
mem_int->set(18);
}
// Example with a dynamic cast
if (Member<float> * f = dynamic_cast<Member<float>*>(member)) {
f->set(3.14f);
std::cout<< "Will never get here since it's a int" << std::endl;
}
}
return 0;
}
I am currently working on a project that reads postcodes from a .csv file, completes a haversine calculation and stores items in a list based on a returned search radius. The postcode variables are defined via a class and uses an interface to impliment in main.cpp
The method that has thrown this all out of place is the operator < overload to provide alphabetical sorting of the postcodes (to be used to sort the list in main.cpp)
Postcode class;
#ifndef POSTCODE_H_
#define POSTCODE_H_
#include <stdexcept> // std::out_of_range
#include "IPostCode.h"
#include <string>
class PostCode : public IPostCode {
private:
int Id;
std::string Postcode;
std::string firstTwoChars;
double Lattitude;
double Longitude;
double distanceFromCentre;
public:
PostCode();
int getId() override;
std::string getPostcode() override;
std::string getFirstTwoChars() override;
double getLattitude() override;
double getLongitude() override;
double getdistanceFromCentre() override;
bool operator<(const PostCode& right) const override;
void setId(std::string newId) override;
void setPostcode(std::string newPostcode) override;
void setLattitude(std::string newLattitude) override;
void setLongitude(std::string newLongitude) override;
void setdistanceFromCentre(double newdistanceFromCentre) override;
void clearPostCode() override;
};
PostCode::PostCode() {
this->Id = 0;
this->Postcode = "";
this->Lattitude = 0.0f;
this->Longitude = 0.0f;
}
int PostCode::getId()
{
return this->Id;
}
std::string PostCode::getPostcode()
{
return this->Postcode;
}
std::string PostCode::getFirstTwoChars()
{
firstTwoChars = Postcode.substr(0, 2);
return this->firstTwoChars;
}
double PostCode::getLattitude()
{
return this->Lattitude;
}
double PostCode::getLongitude()
{
return this->Longitude;
}
double PostCode::getdistanceFromCentre()
{
return this->distanceFromCentre;
}
void PostCode::setId(std::string newId)
{
this->Id = std::stoi(newId);
}
void PostCode::setPostcode(std::string newPostcode)
{
this->Postcode = newPostcode;
}
void PostCode::setLattitude(std::string newLattitude)
{
this->Lattitude = std::stod(newLattitude);
}
void PostCode::setLongitude(std::string newLongitude)
{
this->Longitude = std::stod(newLongitude);
}
void PostCode::setdistanceFromCentre(double newdistanceFromCentre)
{
this->distanceFromCentre = newdistanceFromCentre;
}
void PostCode::clearPostCode() {
this->Id = 0;
this->Postcode = "";
this->Lattitude = 0.0f;
this->Longitude = 0.0f;
}
bool PostCode::operator<(const PostCode& right) const
{
return (Postcode.compare(right.Postcode) < 0);
}
#endif
Interface code;
#ifndef IPOSTCODE_H_
#define IPOSTCODE_H_
#include <string>
class IPostCode {
public:
virtual int getId() = 0;
virtual std::string getPostcode() = 0;
virtual double getLattitude() = 0;
virtual double getLongitude() = 0;
virtual double getdistanceFromCentre() = 0;
virtual std::string getFirstTwoChars() = 0;
virtual bool operator<(const PostCode& right) const = 0;
virtual void setId(std::string newId) = 0;
virtual void setPostcode(std::string newPostcode) = 0;
virtual void setLattitude(std::string newLattitude) = 0;
virtual void setLongitude(std::string newLongitude) = 0;
virtual void setdistanceFromCentre(double newdistanceFromCentre) = 0;
virtual void clearPostCode() = 0;
};
#endif
Errors.
1. Error C2259 'PostCode': cannot instantiate abstract class (This error is for the main.cpp declaration of a PostCode)
2. Error C3668 'PostCode::operator <': method with override specifier 'override' did not override any base class methods (Error within the postcode class)
3. Error C4430 missing type specifier - int assumed. Note: C++ does not support default-int
4. Error C2143 syntax error: missing ',' before '&' (3 + 4 = Errors within the interface)
I've read that the interface errors are due to type identifiers and i should declare them as static but this presents more error's. I am under the assumption that all methods within the interface are to be overridden as they declared pure virtual methods. (i.e = 0;). This isn't a void method because it returns values when implimented.
This does not resolve all the compiler warnings and errors; only the operator<.
Remove the operator< declaration from class IPostCode:
class IPostCode
{
public:
//--------------------------------------------------------
// Remove the function below
//--------------------------------------------------------
virtual bool operator<(const PostCode& right) const = 0;
};
Remove the override keyword from the PostCode class:
class PostCode : public IPostCode
{
public:
//---------------------------------------------------
// Remove "override" from the declaration below.
//---------------------------------------------------
bool operator<(const PostCode& right) const override;
};
You really don't want to override comparison operators or implement comparison functions in base classes. When you have a pointer of the base class type, you don't know what kind of child class it really points to. You could be comparing two different child classes.
I'm writing a class template that can take one of the following, int8_t, int16_t, int32_t, float and double. Here it is:
#ifndef OPERAND_HPP
# define OPERAND_HPP
# include "IOperand.hpp"
# include <regex>
template <typename T>
class Operand : public IOperand {
private:
/* Constructors - Destructors */
Operand() = default;
/* Attributes */
T _num;
eOperandType const _type;
std::string const &_value;
/* Static functions */
static int8_t getValue(Operand<int8_t> const *it) { return it->getNum(); }
static int16_t getValue(Operand<int16_t> const *it) { return it->getNum(); }
static int32_t getValue(Operand<int32_t> const *it) { return it->getNum(); }
static float getValue(Operand<float> const *it) { return it->getNum(); }
static double getValue(Operand<double> const *it) { return it->getNum(); }
public:
/* Constructors - Destructors */
explicit Operand(eOperandType t, std::string const &v, T n) : _num(n), _type(t), _value(v) {}
Operand(Operand const &rhs) : Operand(rhs.getType(), rhs.toString(), rhs.getNum()) {};
~Operand() override = default;
/* Operator overloads */
Operand<T> &operator=(Operand const &rhs) {
_num = rhs.getNum();
_type = rhs.getType();
_value = rhs.toString();
return *this;
}
IOperand const *operator+(IOperand const &rhs) const final {
Operand::getValue(&rhs);
return &rhs;
}
IOperand const *operator-(IOperand const &rhs) const final { return &rhs; }
IOperand const *operator*(IOperand const &rhs) const final { return &rhs; }
IOperand const *operator/(IOperand const &rhs) const final { return &rhs; }
IOperand const *operator%(IOperand const &rhs) const final { return &rhs; }
/* ------------------------------------------------- Exceptions ------------------------------------------------- */
class DivisionByZero : public std::exception {
public:
char const *what() const noexcept final { return "division by zero"; }
};
class ModuloByZero : public std::exception {
public:
char const *what() const noexcept final { return "modulo by zero"; }
};
class Overflow : public std::exception {
public:
char const *what() const noexcept final { return "overflow"; }
};
class Underflow : public std::exception {
public:
char const *what() const noexcept final { return "underflow"; }
};
/* -------------------------------------------------------------------------------------------------------------- */
/* Member functions */
T getNum() const { return _num; }
int getPrecision() const final { return static_cast<int>(_type); }
eOperandType getType() const final { return _type; }
std::string const &toString() const final { return _value; }
};
#endif /* OPERAND_HPP */
That class inherit from the following interface:
#ifndef IOPERAND_HPP
# define IOPERAND_HPP
# include <string>
enum eOperandType {
INT8,
INT16,
INT32,
FLOAT,
DOUBLE
};
class IOperand {
public:
/* Constructors - Destructors */
virtual ~IOperand() = default;
/* Operator overloads */
virtual IOperand const *operator+(IOperand const &) const = 0;
virtual IOperand const *operator-(IOperand const &) const = 0;
virtual IOperand const *operator*(IOperand const &) const = 0;
virtual IOperand const *operator/(IOperand const &) const = 0;
virtual IOperand const *operator%(IOperand const &) const = 0;
/* Member functions */
virtual int getPrecision() const = 0;
virtual eOperandType getType() const = 0;
virtual std::string const &toString() const = 0;
};
#endif /* IOPERAND_HPP */
I'm struggling to implement the + operator overload. That function has two IOperand & as arguments, but I need to access the _num attribute of said arguments. The getter for this attribute isn't in the base class, only in the derived class. I tried polymorphism with the following functions:
static int8_t getValue(Operand<int8_t> const *it) { return it->getNum(); }
static int16_t getValue(Operand<int16_t> const *it) { return it->getNum(); }
static int32_t getValue(Operand<int32_t> const *it) { return it->getNum(); }
static float getValue(Operand<float> const *it) { return it->getNum(); }
static double getValue(Operand<double> const *it) { return it->getNum(); }
but I get the following error message when compiling with clang++:
./Operand.tpp:41:15: error: no matching function for call to 'getValue'
(void)Operand::getValue(&rhs);
./Operand.tpp:19:29: note: candidate function not viable: cannot convert from base class pointer 'const IOperand *' to derived class pointer 'const Operand<int8_t> *'
(aka 'const Operand<signed char> *') for 1st argument
static int8_t getValue(Operand<int8_t> const *it) { return it->getNum(); }
The error message is pretty verbose and I understand where the problem lies. However I'm not sure how to fix it, or if there would be a more elegant approach to my problem. I could probably get away with it using dynamic casts, or even using 5 different classes instead of a template, but it doesn't seem that good.
Could anyone point me in the right direction?