Better way to change class? - c++

I have a program that reads some bytes from a file. If the second byte is exactly 0, it should cast the current type to the correct one.
This is what I have:
struct Event {
virtual ~Event() {}
};
struct MidiEvent : public Event {
unsigned m_channel;
uint8_t byte1;
uint8_t byte2;
MidiEvent(uint8_t channel) : m_channel{ channel } {}
};
struct EventNoteOff : public MidiEvent {
uint8_t note() { return byte1; }
uint8_t velocity() { return byte2; }
EventNoteOff(std::fstream& t_midi_file, uint8_t channel);
};
struct EventNoteOn : public MidiEvent {
uint8_t note() { return byte1; }
uint8_t velocity() { return byte2; }
EventNoteOn(std::fstream& t_midi_file, uint8_t channel);
};
And this is the part where the program decides which class assign to each bytes:
// track_type and track_chanel is a "uint8_t", m_event is a "Event *" and t_midi_file is a std::fstream opened with 'in' and 'binary' flags.
switch (track_type) {
case 0x80:
m_event = new EventNoteOff{ t_midi_file, track_channel };
break;
case 0x90:
t_midi_file.seekg(1, t_midi_file.cur);
char c;
t_midi_file.read(&c, 1);
t_midi_file.seekg(-2, t_midi_file.cur);
if (c == 0)
m_event = new EventNoteOff{ t_midi_file, track_channel };
else
m_event = new EventNoteOn{ t_midi_file, track_channel };
break;
...
}
Whenever track_type is 0x90 and byte2 has the value of 0, i want m_event to behave like a EventNoteOff* with same values as before, not as an EventNoteOn*. My attempt works but I get a feel that this approach is dirty because it involves moving around the file read pointer and creating a temporal variable, when I think it could be done with a reinterpret_cast.
I tried doing
case 0x90:
m_event = new EventNoteOn{ t_midi_file, track_channel };
if(dynamic_cast<EventNoteOn*>(m_event)->velocity() == 0)
m_event = reinterpret_cast<EventNoteOff*>(m_event);
break;
with no luck.
What is a better way to do this?

Related

C++: Find out type of custom class at runtime

I want to create a multimap that maps several bitmaps to their specific char. For latin chars there are more bitmaps (because of font size). Also I need to store chinese chars. There are different fonts (called meg5, meg7, _china01). meg-family fonts are used for latin letters and china01 is used for chinese letters. I need to be able to switch between languages. Thats why I thought of storing them all in one multima. For latin letters I need to determine correct font (between meg5 and meg7).
Here are my classes:
class Bitmap {
public:
virtual ~Bitmap() = default;
inline std::vector<int> getBMPData() const { return m_data; }
inline void setBMPData(std::vector<int> data) { m_data = data; }
private:
std::vector<int> m_data;
};
class MegFamily : public Bitmap {
private:
uint8_t m_OverallSize = 0;
uint8_t m_xDisplacement = 0;
uint8_t m_yDisplacement = 0;
uint8_t m_width = 0;
uint8_t m_height = 0;
public:
MegFamily() {};
inline uint8_t getOverallSize() const { return m_OverallSize; }
inline uint8_t getXDisplacement() const { return m_xDisplacement; }
inline uint8_t getYDisplacement() const { return m_yDisplacement; }
inline uint8_t getWidth() const { return m_width; }
inline uint8_t getHeight() const { return m_height; }
//only for test purposes
inline void setOverallSize(uint8_t overallSize) { m_OverallSize = overallSize; }
inline void setXDisplacement(uint8_t xDisplacement) { m_xDisplacement = xDisplacement; }
inline void setYDisplacement(uint8_t yDisplacement) { m_yDisplacement = yDisplacement; }
inline void setWidth(uint8_t width) { m_width = width; }
inline void setHeight(uint8_t height) { m_height = height; }
};
class Meg5 : public MegFamily {};
class Meg7 : public MegFamily {};
class ChineseFont : public Bitmap{};
I wanted to use e.g. find('A') for all A Bitmaps and then determine their font.
This is what I've done so far.
typedef std::vector<int> BMPData;
std::multimap<char, Bitmap> BMPLibrary;
ChineseFont SomeChineseName;
Meg5 meg5_A;
Meg7 meg7_A;
BMPData BMPSomeChineseName{0x00,0x38,0x27,0x24,0x24,0x24,0x24,0x24,0x24,0xe4,0x04,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x09,0x09,0x08,0x07,0x00,0x00};
BMPData BMPmeg5A{ 0x01, 0x15, 0x05, 0x02 };
BMPData BMPmeg7A{ 0x7E, 0x09, 0x09, 0x09, 0x7E };
SomeChineseName.setBMPData(BMPSomeChineseName);
meg5_A.setBMPData(BMPmeg5A);
meg5_A.setOverallSize(5);
meg5_A.setXDisplacement(0);
meg5_A.setYDisplacement(0);
meg5_A.setWidth(4);
meg5_A.setHeight(5);
meg7_A.setBMPData(BMPmeg5A);
meg7_A.setOverallSize(6);
meg7_A.setXDisplacement(0);
meg7_A.setYDisplacement(0);
meg7_A.setWidth(5);
meg7_A.setHeight(7);
BMPLibrary.insert(std::pair<char, Bitmap>('A', meg5_A));
BMPLibrary.insert(std::pair<char, Bitmap>('A', meg7_A));
BMPLibrary.insert(std::pair<char, Bitmap>('\u2ed8', SomeChineseName));
std::cout << "searching for As" << std::endl;
auto pairFound = BMPLibrary.find('A');
if (pairFound != BMPLibrary.end())
{
size_t numPairsInMap = BMPLibrary.count('A');
for (size_t counter = 0; counter < numPairsInMap; ++counter)
{
std::cout << "Type of found element: " << typeid(pairFound->second).name() << std::endl;
}
}
My output is the following:
searching for As
Type of found element: class Bitmap
Type of found element: class Bitmap
My question is: Is it possible to determine wheter a result is instance of meg5 or meg7?
Thanks for your help.
I think you have a possible option if you don't mind adding some helper functions:
class Bitmap {
...
public:
virtual std::string name() const = 0;
};
...
class Meg7 : MegFamily {
...
public:
std::string name() const override { return "Meg7"; }
};
Then (like some of the other comments have suggested) you'll want to change the
std::multimap<char, Bitmap> BMPLibrary;
...
BMPLibrary.insert(std::pair<char, Bitmap>('A', meg7_A));
to
std::multimap<char, std::unqiue_ptr<Bitmap>> BMPLibrary;
...
BMPLibrary.insert(std::pair<char, std::unqiue_ptr<Bitmap>>('A', std::make_unique<Meg7>(std::move(meg7_A))));
// or you can just use emplace, which is a bit less verbose
BMPLibrary.emplace('A', std::make_unique<Meg7>(std::move(meg7_A)));
since when you use abstract and base classes, you'll generally always need it to be a pointer to the base class, rather than just the base class.
Finally, to get the final name you can simply use:
std::cout << "Type of found element: " << pairFound->second->name() << std::endl;
Thanks for your help.
I changed the class definitions a bit as I think it may be more elegant
class Bitmap {
public:
Bitmap(std::vector<int> BMPData)
: m_data(BMPData)
{}
virtual ~Bitmap() = default;
inline std::vector<int> getBMPData() const { return m_data; }
//inline void setBMPData(std::vector<int> data) { m_data = data; }
void printBMPData() const;
virtual std::string name() const = 0;
protected:
std::vector<int> m_data;
};
class MegFamily : public Bitmap {
protected:
uint8_t m_OverallSize = 0;
uint8_t m_xDisplacement = 0;
uint8_t m_yDisplacement = 0;
uint8_t m_width = 0;
uint8_t m_height = 0;
public:
MegFamily(uint8_t OverallSize, uint8_t xDisplacement, uint8_t yDisplacement, uint8_t width, uint8_t height, std::vector<int> BMPData)
: m_OverallSize(OverallSize), m_xDisplacement(xDisplacement), m_yDisplacement(yDisplacement), m_width(width), m_height(height),
Bitmap(BMPData)
{}
virtual void hello() const { std::cout << "MegFamily" << std::endl; }
inline uint8_t getOverallSize() const { return m_OverallSize; }
inline uint8_t getXDisplacement() const { return m_xDisplacement; }
inline uint8_t getYDisplacement() const { return m_yDisplacement; }
inline uint8_t getWidth() const { return m_width; }
inline uint8_t getHeight() const { return m_height; }
};
class Meg5 : public MegFamily
{
public:
virtual void hello() const { std::cout << "Meg5" << std::endl; }
std::string name() const override { return "Meg5"; }
Meg5(uint8_t OverallSize, uint8_t xDisplacement, uint8_t yDisplacement, uint8_t width, uint8_t height, std::vector<int> BMPData)
: MegFamily{ OverallSize, xDisplacement, yDisplacement, width, height, BMPData} {};
~Meg5() {};
};
class Meg7 : public MegFamily
{
public:
virtual void hello() const { std::cout << "Meg7" << std::endl; }
std::string name() const override { return "Meg7"; }
Meg7(uint8_t OverallSize, uint8_t xDisplacement, uint8_t yDisplacement, uint8_t width, uint8_t height, std::vector<int> BMPData)
: MegFamily{ OverallSize, xDisplacement, yDisplacement, width, height, BMPData} {};
~Meg7() {};
};
class ChineseFont : public Bitmap
{
public:
ChineseFont(std::vector<int> BMPData)
: Bitmap(BMPData) {};
std::string name() const override { return "ChineseFont"; }
};
Additionally I implemented your recommended ideas
BMPData BMPSomeChineseName{0x00,0x38,0x27,0x24,0x24,0x24,0x24,0x24,0x24,0xe4,0x04,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x09,0x09,0x08,0x07,0x00,0x00 };
BMPData BMPmeg5A{ 0x01, 0x15, 0x05, 0x02 };
BMPData BMPmeg7A{ 0x7E, 0x09, 0x09, 0x09, 0x7E };
Meg5 meg5_A = Meg5(5, 0, 0, 4, 5, BMPmeg5A);
Meg7 meg7_A = Meg7(6,0,0,5,7, BMPmeg7A);
ChineseFont SomeChineseName = ChineseFont(BMPSomeChineseName);
std::multimap<char, std::unique_ptr<Bitmap>> BMPLibrary;
BMPLibrary.emplace('A', std::make_unique<Meg5>(std::move(meg5_A)));
BMPLibrary.emplace('A', std::make_unique<Meg7>(std::move(meg7_A)));
BMPLibrary.emplace('\u2ed8', std::make_unique<ChineseFont>(std::move(SomeChineseName)));
Now it's possible to determine the type at runtime.
Thank you!

Unhelpful errors in Tinkercad writing Arduino c++ code

Sorry if some of my source looks really bad. This is my first attempt writing Arduino code as well as c++. I usually stay in my comfort zone of c#.
I'm getting the following errors when trying to run my source on a Tinkercad circuit. The way Tinkercad spits out errors is horrible for learning. Anyone who might be able to point out my mistakes that it is having a fit with would be very helpful.
Errors
- 2:23: error: use of enum 'LedActionTypes' without previous
declaration
- 4:23: error: use of enum 'SignalTypes' without previous declaration
- 4:64: error: use of enum 'SignalDirections' without previous declaration
- 5:23: error: use of enum 'SignalTypes' without previous declaration
- 5:64: error: use of enum 'SignalDirections' without previous declaration
- 5:103: error: use of enum 'DigitalSignalValues' without previous declaration
- 8:16: error: variable or field 'AddAction' declared void
- 8:16: error: 'LedAction' was not declared in this scope
My source code
/* Enums */
enum SignalTypes
{
General = 0,
Analog = 1,
Digital = 2,
};
enum SignalDirections
{
Inbound = 0x0,
Outbound = 0x1,
};
enum DigitalSignalValues
{
Low = 0x0,
High = 0x1,
};
enum LedActionTypes
{
None = 0,
ChangeBrightness = 1,
};
/* LED Action object */
class LedAction {
// Functions
void Constructor(enum LedActionTypes action, int intensity, int delay)
{
// Check for valid intensity value
if (intensity < 0)
intensity = 0;
if (intensity > 255)
intensity = 255;
Intensity = intensity;
Finished = false;
Action = action;
CompleteOn = millis() + delay;
}
public:
// Properties
boolean Finished;
enum LedActionTypes Action = None;
int Intensity = 0;
unsigned long CompleteOn = 0;
// Constructor
LedAction()
{
Constructor(Action, Intensity, 0);
}
LedAction(enum LedActionTypes action)
{
Constructor(action, Intensity, 0);
}
LedAction(enum LedActionTypes action, int intensity, int delay)
{
Constructor(action, intensity, delay);
}
// Methods
void Loop() {
if (Finished) { return; }
unsigned long currentTimeStamp = millis();
if (CompleteOn >= currentTimeStamp)
{
//Process the action
Finished = true;
}
}
};
/* LED object */
class Led {
// Functions
void Constructor(enum SignalTypes signalType, byte pbPin, enum SignalDirections signalDirection, int intensity)
{
// Check for valid intensity value
if (intensity < 0)
intensity = 0;
if (intensity > 255)
intensity = 255;
Intensity = intensity;
Constructor(SignalType, PBPin, SignalDirection, DigitalSignalValue);
}
void Constructor(enum SignalTypes signalType, byte pbPin, enum SignalDirections signalDirection, enum DigitalSignalValues signalValue)
{
SignalType = signalType;
PBPin = pbPin;
SignalDirection = signalDirection;
DigitalSignalValue = signalValue;
}
public:
// Properties
byte PBPin;
int Intensity;
enum DigitalSignalValues DigitalSignalValue;
enum SignalTypes SignalType = Analog;
enum SignalDirections SignalDirection = Outbound;
LedAction Actions[20]{ LedAction(None) };
// Constructor
Led()
{
Constructor(SignalType, 0, SignalDirection, 0);
}
Led(byte pbPin, enum SignalDirections signalDirection, int intensity)
{
Constructor(SignalType, pbPin, signalDirection, intensity);
}
Led(byte pbPin, enum SignalDirections signalDirection, enum DigitalSignalValues signalValue)
{
Constructor(SignalType, pbPin, signalDirection, signalValue);
}
Led(enum SignalTypes signalType, byte pbPin, enum SignalDirections signalDirection, int intensity)
{
Constructor(signalType, pbPin, signalDirection, intensity);
}
Led(enum SignalTypes signalType, byte pbPin, enum SignalDirections signalDirection, enum DigitalSignalValues signalValue)
{
Constructor(signalType, pbPin, signalDirection, signalValue);
}
// Methods
void Setup()
{
switch (SignalType)
{
case Analog:
analogWrite(PBPin, Intensity);
break;
case Digital:
digitalWrite(PBPin, Intensity);
pinMode(PBPin, SignalDirection);
break;
}
}
void Loop()
{
int n;
// Loop through all actions and find unfinished ones then fire them off
for ( n=0 ; n<20 ; ++n )
{
if (!Actions[n].Finished)
{
Actions[n].Loop();
if (Actions[n].Finished)
{
//Action was just flagged ready to process
switch (Actions[n].Action)
{
case ChangeBrightness:
digitalWrite(PBPin, Actions[n].Intensity);
break;
}
}
}
}
}
void AddAction(LedAction action)
{
int n;
// Loop through all actions and find an unfinished one then reuse it
for (n = 0; n < 20; ++n)
{
if (!Actions[n].Finished)
{
action.Finished = false;
Actions[n] = action;
break;
}
}
}
void ClearAllActions()
{
int n;
// Loop through all actions and mark all as finished
for (n = 0; n < 20; ++n)
{
if (!Actions[n].Finished)
{
Actions[n].Finished = true;
}
}
}
};
An example of the code on Tinkercad Circuits can be found at...
https://www.tinkercad.com/things/gmGeFVKOA3e-adrunio-test/editel
Once on the page, click the "Simulate" play button (bottom left) and then hit the "Start Simulation" button at the top. You should see the same errors I'm seeing.
OK, so I think I found my answer...
After having a few conversations with Tinkercad and the great support team there I was told that they use an old version of Arduino to compile their code. Well, all the code I'm writing and the IDE I'm writing in are based on the latest releases of everything. I think this is where my issue starts to happen.
Currently, when trying to compile the following code in the current version that Tinkercad uses I get the respective error that follows...
enum TestEnum
{
OptOne = 1,
OptTwo = 2
};
class TestClass {
private:
void InitilizeClass(enum TestEnum te)
{ }
public:
TestClass(enum TestEnum te)
{ }
};
void setup()
{ }
void loop()
{ }
As you can see, I'm trying to create a function in my class called InitializeClass() that takes my enum as a parameter. The compiler does not like that one bit, and the error highlighting along with the error returned are horrible for some reason telling me nothing!
However, if I change the class to only use the enum in the constructor itself, that seems to compile just fine with no errors.
enum TestEnum
{
OptOne = 1,
OptTwo = 2
};
class TestClass {
private:
void InitilizeClass()
{ }
public:
TestClass(enum TestEnum te)
{ }
};
void setup()
{ }
void loop()
{ }
The Problem
I wanted to create a class that has as least repetitive code as possible, this is usually the way I try and code in general. If I have to type the same code twice... Start looking at making it a function. This way a) there is less code and b) if you ever need to change the logic you're changing it in one place rather than multiple places. So I start out usually making my classes look like this...
enum TestEnum
{
OptOne = 1,
OptTwo = 2
};
class TestClass {
private:
void InitilizeClass(enum TestEnum te, int n, int t)
{
if (n < 0)
n = 0;
if (n > 255)
n = 255;
TE = te;
N = n;
T = t;
}
public:
enum TestEnum TE = OptOne;
int N = 0;
int T = 0;
TestClass(enum TestEnum te)
{ InitilizeClass(te, 0, 0); }
TestClass(enum TestEnum te, int n)
{ InitilizeClass(te, n, 0); }
TestClass(enum TestEnum te, int n, int t)
{ InitilizeClass(te, n, t); }
};
void setup()
{ }
void loop()
{ }
This works perfectly fine in VS IDE (c++) as well as the latest Arduino IDE. However, since Tinkercad is using an old Arduino compiler it has a fit with me using the enums in a private class function. (Update: Just found out this happens in ANY class function! Not just a private one.)
The Solution
So this whole time, I have been thinking with a c# mentality when coding my Arduino project when I should have been thinking more c++ like. Well, in my defense I am after all a c# guy through and through. I love it SO much more the c++. Well in almost every way. There are apparently exceptions. I recently found out, that while c# does not allow it c++ DOES allow you to overload class constructors! That changes everything for the way I would write my classes in c++. Now there is no reason for the private class function to initialize the class. So I can just have something like the following...
enum TestEnum
{
OptOne = 1,
OptTwo = 2
};
class TestClass {
public:
enum TestEnum TE = OptOne;
int N = 0;
int T = 0;
TestClass(enum TestEnum te)
{ TestClass(te, 0, 0); }
TestClass(enum TestEnum te, int n)
{ TestClass(te, n, 0); }
TestClass(enum TestEnum te, int n, int t)
{ TestClass(te, n, t); }
};
void setup()
{ }
void loop()
{ }
That my friends compiles completely fine in the old Arduino compilers. It also saves me some space for other code (these ATTiny chips are not huge so a smaller program is always a win!).

How I can cast an uint8 to a struct representing a byte?

I have a struct representing a byte having a fix offset 0b1111. I have defined a conversion operator to cast the SByte to unit8.
How can I have the opposite conversion (from uint8 to SByte)?
Is the only solution to replace the SByte with a uint8_t or maybe a union in the first place?
struct SByte
{
uint8_t offset : 4;
uint8_t bit4 : 1;
uint8_t bit5 : 1;
uint8_t bit6 : 1;
uint8_t bit7 : 1;
SByte():offset(15), bit4(0), bit5(0), bit6(0), bit7(0){}
explicit operator int8_t() const
{
return static_cast<uint8_t>((bit7 << 7) | (bit6 << 6) | (bit5 << 5) | (bit4 << 4));
}
};
int main()
{
auto lbyte = SByte();
auto result = static_cast<int8_t>(lbyte);
assert(result == 15); // 0b00001111
}
How can I have the opposite conversion (from uint8 to SByte)?
By defining a constructor:
explicit SByte(uint8_t byte) {
// extract individual bits from `byte`
}

How to get data from a specific class/member location in the main program from a loaded DLL/Shared Library? [Windows/Linux]

I am trying to call a function which is located in the program from a DLL.
The program is closed source but the structures are know.
I need to call a function called "GetPlayerPosition" which looks like this:
// native GetPlayerPos(playerid, &Float:x, &Float:y, &Float:z)
static cell AMX_NATIVE_CALL n_GetPlayerPos(AMX *amx, cell *params)
{
CHECK_PARAMS(4);
CPlayer* pPlayer = pNetGame->GetPlayerPool()->GetAt((BYTE)params[1]);
if (pPlayer)
{
cell* cptr;
amx_GetAddr(amx, params[2], &cptr);
*cptr = amx_ftoc(pPlayer->m_vecPos.X);
amx_GetAddr(amx, params[3], &cptr);
*cptr = amx_ftoc(pPlayer->m_vecPos.Y);
amx_GetAddr(amx, params[4], &cptr);
*cptr = amx_ftoc(pPlayer->m_vecPos.Z);
return 1;
} else {
return 0;
}
}
I want to call this part from my DLL/SO:
pNetGame->GetPlayerPool()->GetAt((BYTE)<my own input data here>);
I know the streucture/classes are this:
typedef struct _VECTOR {
float X,Y,Z;
} VECTOR, *PVECTOR;
CNetGame *pNetGame = NULL;
class CNetGame
{
private:
CPlayerPool *m_pPlayerPool;
public:
CNetGame();
~CNetGame();
CPlayerPool * GetPlayerPool() { return m_pPlayerPool; };
};
void CNetGame::Init(BOOL bFirst = false)
{
// Setup player pool
if(!m_pPlayerPool) {
m_pPlayerPool = new CPlayerPool();
} else {
m_pPlayerPool->ResetPlayerScoresAndMoney();
}
}
class CPlayerPool
{
private:
BOOL m_bPlayerSlotState[MAX_PLAYERS];
CPlayer *m_pPlayers[MAX_PLAYERS];
public:
CPlayerPool();
~CPlayerPool();
BOOL New(BYTE bytePlayerID, PCHAR szPlayerName);
BOOL Delete(BYTE bytePlayerID, BYTE byteReason);
// Retrieve a player
CPlayer* GetAt(BYTE bytePlayerID) {
if (bytePlayerID >= MAX_PLAYERS) { return NULL; }
return m_pPlayers[bytePlayerID];
};
};
class CPlayer
{
private:
BYTE m_bytePlayerID;
public:
CPlayer();
~CPlayer() {};
VECTOR m_vecPos;
};
So how would I call pNetGame->GetPlayerPool()->GetAt((BYTE)<my own input data here>); with this setup?

Working with a void pointer

Given the following scenario where my data might be of different type based on some condition.
class myClass {
public:
myclass() {
if (condition1) {
bool boolValue = false;
data = boolValue;
} else if (condition2) {
int intValue = 0;
data = intValue;
} else if (condition3) {
unsigned int unsignedIntValue = 0;
data = unsignedIntValue;
} else if (condition4) {
long longValue = 0;
data = longValue;
} else if (condition5) {
double doubleValue = 0.0;
data = doubleValue;
} else if (condition6) {
float floatValue = 0.0;
data = floatValue;
} else if (condition7) {
char *buffer = new char[10];
data = buffer;
}
}
void* getData() const { return data; }
private:
void *data;
}
As it happens the value that my void pointer points to is strictly within each statement. Therefore what is returned with getData() might not be valid. If I do get the data it is simply because the memory location where I point to is not yet written over.
The solution I have come up with is this:
class myClass {
public:
myclass() {
if (condition1) {
boolValue = false;
data = boolValue;
} else if (condition2) {
intValue = 0;
data = intValue;
} else if (condition3) {
unsignedIntValue = 0;
data = unsignedIntValue;
} else if (condition4) {
longValue = 0;
data = longValue;
} else if (condition5) {
doubleValue = 0.0;
data = doubleValue;
} else if (condition6) {
floatValue = 0.0;
data = floatValue;
} else if (condition7) {
buffer = new char[10];
data = buffer;
}
}
void* getData() const { return data; }
private:
void *data;
bool boolValue;
int intValue;
unsigned int unsignedIntValue;
long longValue;
double doubleValue;
float floatValue;
char *buffer;
}
I was thinking there must be a more elegant way to do this. Any suggestions?
You could use a union to save a few bits in memory, and then use pointer casting to get the value from the union:
#include<iostream>
using namespace std;
class myClass {
public:
myClass(char *str){
data.str = str;
}
myClass(double d){
data.d = d;
}
myClass(float f){
data.f = f;
}
void *getData() { return (void*)&data; }
private:
union {
double d;
float f;
char *str;
} data;
};
int main(){
myClass c(2.0);
cout << *(double*)c.getData() << endl;
myClass f(3.0f);
cout << *(float*)f.getData() << endl;
myClass s("test");
cout << *(char**)s.getData() << endl;
system("pause");
}
/* prints
2
3
test
*/
If you don't need to change the type of the data after you create an object, then you could use a template class:
template <typename T>
class myBaseClass {
public:
// Declare common functions here.
T getData()
{ return data; }
protected:
T data;
protected:
// Disallow constructing instances of this class outside the child classes.
myBaseClass(T val) : data(val) { }
};
template <typename T>
class myClass: public myBaseClass<T> {
public:
myClass() : myBaseClass<T>(0) { }
};
You then specialize for char*:
template <>
class myClass<char*>: public myBaseClass<char*> {
public:
myClass() : myBaseClass(new char[10]) { }
};
You then create instances like this:
myClass<int> a;
myClass<float> b;
myClass<char*> c;
// etc.
int i = a.getData();
float f = b.getData();
char* str = c.getData();