Problem with using derived class where base class is expected - c++

I am writing a code for a little system that should run on an arduino. The objective is to control several cycles which each have a certain amount of sub-cycles. Both the cycles and the subcycles are defined by their duration and ultimately, the system's operations will be performed at the subcycle level (didn't implement this yet).
I thought about creating a class that could manage those events called EventManager. A class to represent the subevents called Events and a class called Cycles which is derived from both classes because it is both an event and at the same time will manage the subevents.
My problem is that when I pass Event objects to the EventManager, all is good. However, when I pass the Cycle objects, it doesn't work as expected (see attached pictures... Cycle 2 name isn't initialized for some reason).
This is my code below and please bare with me, I know it's a lot of files. Any help would be greatly appreciated. Thank you!
EventManager.h
#ifndef SRC_EVENTMANAGER
#define SRC_EVENTMANAGER
#include <Arduino.h>
#include "Event.h"
class EventManager
{
public:
EventManager(size_t n_events = 0, Event *events = nullptr);
void loop();
private:
size_t n_events;
Event *events;
size_t current_event;
bool cycle_ended;
bool check_event_end();
void end_current_event();
};
#endif /* SRC_EVENTMANAGER */
EventManager.cpp
#include "EventManager.h"
EventManager::EventManager(size_t n_events, Event *events)
: n_events(n_events), events(events), current_event(0), cycle_ended(false) {}
bool EventManager::check_event_end()
{
return events[current_event].ended();
}
void EventManager::end_current_event()
{
events[current_event].end();
current_event == n_events - 1 ? cycle_ended = true : current_event++;
}
void EventManager::loop()
{
if (cycle_ended)
return;
events[current_event].run();
if (check_event_end())
end_current_event();
}
Event.h
#ifndef SRC_EVENT
#define SRC_EVENT
#include <Arduino.h>
#include "utils.h"
class Event
{
public:
Event(String name, Duration duration);
bool ended();
void start();
void run();
void end();
private:
String name;
unsigned long duration;
unsigned long start_time;
unsigned long end_time;
bool started;
};
#endif /* SRC_EVENT */
Event.cpp
#include "Event.h"
Event::Event(String name, Duration duration)
: name(name), start_time(0), end_time(0), started(false)
{
this->duration = duration.toMillis();
}
bool Event::ended()
{
return millis() >= end_time;
}
void Event::start()
{
if (started)
return;
start_time = millis();
end_time = start_time + duration;
Serial.println("Event " + name + " started.");
started = true;
}
void Event::end()
{
start_time = 0;
end_time = 0;
started = false;
Serial.println("Event " + name + " ended.");
}
void Event::run()
{
start();
// Event logic here
}
Cycle.h
#ifndef SRC_CYCLE
#define SRC_CYCLE
#include <Arduino.h>
#include "Event.h"
#include "EventManager.h"
class Cycle : public Event, public EventManager
{
public:
Cycle(String name, Duration duration, size_t event_count, Event *events);
};
#endif /* SRC_CYCLE */
Cycle.cpp
#include "Cycle.h"
Cycle::Cycle(String name, Duration duration, size_t event_count, Event *events)
: Event(name, duration), EventManager(event_count, events){}
main.cpp
#include <Arduino.h>
#include "EventManager.h"
#include "Event.h"
#include "Cycle.h"
Event events[] = {
Event("event1", Duration{0, 0, 5}),
Event("event2", Duration{0, 0, 5}),
};
Cycle cycles[] = {
Cycle("First cycle", Duration{0, 0, 10}, 2, events),
Cycle("Second cycle", Duration{0, 0, 10}, 2, events),
};
EventManager event_manager(2, cycles);
// EventManager event_manager(2, events);
void setup()
{
Serial.begin(9600);
}
void loop()
{
event_manager.loop();
}

You aren't passing an event or event pointer to your event manager; you are passing an array of events. While accessing individual objects through a pointer is polymorphic, this does not extend to raw arrays. Raw arrays are simple collections of only 1 type of object (naturally all of the same size). And all they contain is the objects - not the type or number of entries. (More favored in modern C++ are the fancier std::array and std::vector which you might want to look into, but won't solve your immediate problem here.)
You can't substitute an array of derived class for an array of base class. While objects accessed via pointer behave polymorphically, an array of a derived class is not a sub type of an array of a base class. If you pass a raw array, as you are doing, by pointer to first element and number of elements, that is fine as the size of each element is implicit in the element type. If you substitute in an array of a derived type, the compiler will be calculating the wrong address for any element after the first. You then get a hearty helping of scrambled data. I should also mention that modern C++ isn't particularly welcoming to unsanctioned type-punning games.
What you can do is set things up to pass an array of base pointers to derived objects. That is entirely legitimate and keeps to the same level language features that you are currently using. This adds an extra level of indirection and an intermediate array to your design.
To try to illustrate this suggestion, some key modified snippets from your code keeping things simple and close to your code (but not updating all of it):
EventManager.h
#ifndef SRC_EVENTMANAGER
#define SRC_EVENTMANAGER
#include <Arduino.h>
#include "Event.h"
class EventManager
{
public:
EventManager(size_t n_events = 0, Event **eventPointers = nullptr); // changed
void loop();
private:
size_t n_events;
Event **eventPointers; // changed
size_t current_event;
bool cycle_ended;
bool check_event_end();
void end_current_event();
};
#endif /* SRC_EVENTMANAGER */
partial EventManager.cpp
#include "EventManager.h"
EventManager::EventManager(size_t n_events, Event **eventPointers)
: n_events(n_events), eventPointers(eventPointers), current_event(0), cycle_ended(false) {}
// changed
bool EventManager::check_event_end()
{
return eventPointers[current_event]->ended(); // changed
}
Event.h
#ifndef SRC_EVENT
#define SRC_EVENT
#include <Arduino.h>
#include "utils.h"
class Event
{
public:
Event(String name, Duration duration);
virtual bool ended(); // make functions virtual as needed
virtual void start();
virtual void run();
virtual void end();
private:
String name;
unsigned long duration;
unsigned long start_time;
unsigned long end_time;
bool started;
};
#endif /* SRC_EVENT */
main.cpp
#include <Arduino.h>
#include "EventManager.h"
#include "Event.h"
#include "Cycle.h"
Event events[] = {
Event("event1", Duration{0, 0, 5}),
Event("event2", Duration{0, 0, 5}),
};
Event *eventsPointers[] = { // added
&events[0],
&events[1],
};
Cycle cycles[] = {
Cycle("First cycle", Duration{0, 0, 10}, 2, events),
Cycle("Second cycle", Duration{0, 0, 10}, 2, events),
};
Event *cyclesPointers[] = { // added, note using base pointers
&cycles[0],
&cycles[1],
};
EventManager event_manager(2, cyclesPointers);
// EventManager event_manager(2, eventsPointers);

Related

C++ passing class to constructor = not passing same instance?

It seems that, when I pass an class it is not passing a persistant (the same) instance of that class as I would expect. I'm assuming this has something to do with memory state but I would appreciate it if someone could explain exactly what is happening. The issue is easily demonstrated as follows :
Main.ino
#include "Debug.h"
#include "Box.h"
Debug debug;
Box box(debug);
void loop(){
debug.message("loop");
debug.line();
}
void setup(){
debug.init();
box.init();
debug.message("Setup Complete");
debug.line();
}
Debug.h
#ifndef DEBUG_H
#define DEBUG_H
class Debug {
private:
bool state;
public:
Debug();
void init();
void message(const char *str);
void message(int);
void line();
};
#endif
Debug.cpp
#include "Debug.h"
#include <Arduino.h>
Debug::Debug() : state(false) {}
void Debug::init() {
if (state == false){
Serial.begin(9600);
state = true;
}
}
void Debug::message(const char *messageChar) {
if (state){
const char *p;
p = messageChar;
while (*p) {
Serial.print(*p);
p++;
}
}
}
void Debug::message(int messageInt) {
if (state){
Serial.print(messageInt);
}
}
void Debug::line() {
if (state){
Serial.println();
}
}
Box.h
#ifndef BOX_H
#define BOX_H
#include "Debug.h"
class Box {
private:
Debug debug;
public:
Box(Debug &debug);
void init();
};
#endif
Box.cpp
#include "Box.h"
#include <Arduino.h>
Box::Box(Debug &debug):
debug(debug)
{}
void Box::init(){
// Switches
pinMode(28, INPUT_PULLUP);
debug.message("Box intialized");
debug.line();
}
So the above code outputs to serial:
Setup Complete
If I modify Box::init() to
void Box::init(){
// Switches
pinMode(28, INPUT_PULLUP);
debug.init();
debug.message("Box intialized");
debug.line();
}
I get what I want :
Box initialized
Setup Complete
If I get rid of Box constructor class and instead do
void Box::init(Debug &debug){
this->debug = debug;
// Switches
pinMode(28, INPUT_PULLUP);
debug.message("Box intialized");
debug.line();
}
Called via Main.ino like
void setup(){
debug.init();
box.init(debug);
debug.message("Setup Complete");
debug.line();
}
I get the desired response again. I don't understand why my first attempt doesn't work nor do I feel comfortable knowing what best practices are. I would appreciate any guidance.
You have two Debug values in your code. One global, one member of the Box class.
Those are two distinct values, since Box create or copy from a value to create its own, and there's the global one.
A solution would be to contain a reference or a pointer.
Here's the example with a reference:
class Box {
private:
Debug& debug;
// ^---- there
public:
Box(Debug &debug);
void init();
};
If you want Box to still be assignable, then use a pointer:
class Box {
private:
Debug* debug;
// ^---- now it's a star
public:
Box(Debug &debug);
void init();
};
Box::Box(Debug &debug):
debug(&debug)
{} // ^----- here, we take the address of the debug variable.
Since references are immutable, you loose some important feature of the language: assignment.
struct thing {
int& ref;
};
int main () {
int a, b;
thing t1{a}, t2{b};
t1 = t2; // ERROR!
}
The assignment would cause t1.ref to point to b after the assignment.
Pointer has a more difficult syntax and hard to guess semantics. However, they play very well with assignment since they give you more freedom:
struct thing {
int* ptr;
};
int main () {
int a, b;
thing t1{&a}, t2{&b};
t1 = t2; // Works, t1.ptr points to b
}

Crash: When accessing vector from class

I'm programming an ESP32 UI and have run into this issue:
I have a class Menu, and define a vector _menuItemStack in its header.
I create an Object MenuItem from outside and pass it to the function addMenuItem(MenuItem menuItem). It adds it to the stack. Which works, when I declare the vector in the Menu.cpp it runs. But it won't be unique for each object, I think it becomes static?
So I declared the vector as private in the header, while it will compile. it crashes immediately.
What is my mistake? C++ makes my head hurt.
Menu.h
/*
Menu.h - Menu Class
*/
#ifndef Menu_h
#define Menu_h
#include "Arduino.h"
#include "StackArray.h"
#include "MenuItem.h"
#include "SPI.h"
#include "U8g2lib.h"
class Menu
{
public:
Menu();
void update();
void draw();
void addMenuItem(MenuItem menuItem);
private:
int arrayPos;
std::vector<MenuItem> _menuItemStack;
};
#endif
Menu.cpp
#include "Menu.h"
extern U8G2_SSD1327_MIDAS_128X128_2_4W_HW_SPI u8g2;
Menu::Menu() {
arrayPos = 0;
}
void Menu::draw() {
u8g2.setFont(u8g2_font_6x10_tf);
int posY = 0;
for (MenuItem &m : _menuItemStack){
u8g2.drawStr(0,posY+15,m.getText().c_str());
posY+=15;
}
}
void Menu::update() {
}
void Menu::addMenuItem(MenuItem menuItem){
arrayPos++;
_menuItemStack.push_back(menuItem);
//debug
Serial.println(arrayPos);
Serial.println(menuItem.getText());
}
Notes: the std::stdlib is included higher up.
EDIT:
MenuItem.cpp
#include "MenuItem.h"
extern U8G2_SSD1327_MIDAS_128X128_2_4W_HW_SPI u8g2;
MenuItem::MenuItem() {
_text = new String;
}
MenuItem::MenuItem(const MenuItem &obj){
_text = new String;
*_text = *obj._text;
}
void MenuItem::draw(){
}
void MenuItem::update(){
}
void MenuItem::setText(String txt){
*_text = txt;
}
String MenuItem::getText(){
return *_text;
}
void MenuItem::attachHandler(CallbackFunction f){
callback = f;
}
void MenuItem::run(){
if (callback != NULL) callback();
}
MenuItem.h
#ifndef MenuItem_h
#define MenuItem_h
#include "Arduino.h"
#include "StackArray.h"
#include "SPI.h"
#include "U8g2lib.h"
class MenuItem
{
private:
typedef void (*CallbackFunction)();
CallbackFunction callback = NULL;
String *_text;
public:
MenuItem();
MenuItem(const MenuItem &obj);
void draw();
void update();
void run();
void setText(String txt);
void attachHandler(CallbackFunction f);
String getText();
};
#endif
The menuItem passed into the addMenuItem function is a copy that has a lifespan until the end of that function.
Try passing in the menu item as a reference so that your list of menu items will be populated with objects that have a lifespan longer than the addMenuItem function. That change the signature to look like this:
void Menu::addMenuItem(MenuItem& menuItem)
When you call std::vector::push_back you create a copy of your object and store it into the vector. So first check, the copy constructor of your class and if the error could be caused by this.
Then you need to know where is stored the memory necessary for std::vector.
On embedded target you may not be using the traditional standard library and manipulating memory can be tricky.
When you declare your vector as a global variable, it is highly probable that the memory required is not in the same zone than when you declare it as a private member of a class. This will depend on your platform, compiler, linker script, and the lib C++ your are using. std::vector use an allocator, on Linux you can get away without looking at what it does, when on embedded target you need to know which allocator your are using and where the memory is.
You can try to print the address of std::vector::data() to check that. Then you will probably have to either provide your own allocator or to reserve a portion of memory large enough to hold the vector and initialize the data contained in your vector at this memory address.

Connect member function from unknown class to boost signal

I am aware that there is quite a few topics around this, but I just can't figure it out.
I am working for a Signal driven input manager for my game and since I need to control multiple players, I need to connect a member function of a PlayerController instance to my vector of signals.
Problem is, that there is not only going to be a PlayerController, but also a MenuController etc. So I will need to connect a member function of a MenuController to the signal aswell.
I am pretty sure I am close to the solution, but I just can't seem to figure it out.
Can someone help me with the registerEvent function signature and how to call the connect method.
InputManager.tcc
#include "boost/function.hpp"
template<class T>
void InputManager::registerEvent(SDL_Keycode key,KeyState state,boost::function<void ()> const& function)
{
auto &inputEvents = (state == KeyState::Up) ? m_keyUpEvents : m_keyDownEvents;
if(inputEvents.find(key) == inputEvents.end())
{
inputEvents.insert(std::make_pair(key, boost::signals2::signal<void()>()));
m_keyStates[key] = KeyState::Up;
}
inputEvents[key].connect(boost::bind(T::function, instance));
}
InputManager.hpp:
#ifndef SSB_INPUTMANAGER_HPP
#define SSB_INPUTMANAGER_HPP
#include <functional>
#include <algorithm>
#include <vector>
#include <map>
#include <SDL_keycode.h>
#include <SDL_events.h>
#include <boost/signals2.hpp>
enum KeyState{
Down,
Up
};
class InputManager
{
public:
InputManager();
template<class T>
void registerEvent(SDL_Keycode key,KeyState state, boost::function<void ()> const& function);
void pollEvent(SDL_Event event);
private:
std::map<SDL_Keycode, boost::signals2::signal<void ()>> m_keyDownEvents;
std::map<SDL_Keycode, boost::signals2::signal<void ()>> m_keyUpEvents;
std::map<SDL_Keycode, KeyState> m_keyStates;
};
#include "InputManager.tcc"
#endif //SSB_INPUTMANAGER_HPP
PlayerController.hpp
#ifndef SSB_PLAYERCONTROLLER_HPP
#define SSB_PLAYERCONTROLLER_HPP
class PlayerController
{
public:
void jump();
private:
Player m_player;
};
#endif //SSB_PLAYERCONTROLLER_HPP
Then I want to call in an initialization method somewhere in my game:
PlayerController playerController;
InputManager inputController;
inputController.registerEvent(SDLK_0, KeyState::Down, playerController.jump());
Both boost::function<> and std::function<> already do type erasure. This means that they "abstract" away any bound parameters.
The this* argument is really just that: a parameter. So, the same goes for it.
Live On Coliru (c++11)
#include <boost/function.hpp>
#include <boost/signals2.hpp>
#include <map>
#include <iostream>
enum SDL_Keycode { K_A, K_B, K_C, K_Up, K_Down, K_Right, K_Left, K_LCtrl, /*etc....*/ };
enum KeyState { Down, Up };
struct InputManager {
void registerEvent(SDL_Keycode key, KeyState state, boost::function<void()> const &function) {
auto& map = state == Up? m_keyUpEvents : m_keyDownEvents;
map[key].connect(function);
}
void poll_event() {
// hardcoded for demo
m_keyDownEvents[K_B]();
}
private:
std::map<SDL_Keycode, boost::signals2::signal<void()>> m_keyDownEvents;
std::map<SDL_Keycode, boost::signals2::signal<void()>> m_keyUpEvents;
std::map<SDL_Keycode, KeyState> m_keyStates;
};
struct Player {};
class PlayerController {
public:
void jump() {
std::cout << "Player jumped\n";
}
private:
Player m_player;
};
class CowController {
public:
void moo() {
std::cout << "Cow mooed\n";
}
};
int main() {
InputManager inputController;
PlayerController p;
inputController.poll_event(); // nothing
inputController.registerEvent(K_B, Down, [&] { p.jump(); });
inputController.poll_event(); // player jumps
CowController c;
inputController.registerEvent(K_B, Down, [&] { c.moo(); });
inputController.poll_event(); // player jumps, cow moos
}
Prints
Player jumped
Player jumped
Cow mooed
C++03
If you don't have lambdas, you can use Boost Bind (or std::tr1::bind):
inputController.registerEvent(K_B, Down, boost::bind(&PlayerController::jump, boost::ref(p)));
inputController.poll_event(); // player jumps
CowController c;
inputController.registerEvent(K_B, Down, boost::bind(&CowController::moo, boost::ref(c)));
See it Live On Coliru

Polymorphic classes not behaving as expected

I'm programming some arduino code, but things aren't quite going to plan.
What am I doing wrong here? I've read around and tried to educate myself about virtual functions, but perhaps I've missed something. Go to QUESTIONSHERE for the actual questions to which I need answers, but first, some explanation:
The Classes RGBPixel and colorGenerator both derive from colorSource, which provides public functions getR(), getG() and getB() so that another pixel or color modifier can take a copy of their current colour.
Classes derived from colorGenerator implement the colour generation code so that they can generate their own colour, while RGBPixels have a colorSource *parent member, so they can obtain a color value from a colorGenerator or another RGBPixel.
In my example, I have one colorGenerator subclass (CG_EmeraldWaters, which should create me a variety of greens and blues), and then a number of RGBPixels in an array. RGBPixels[0] should take its value from an instance of GC_EmeraldWaters, while RGBPixels[1] takes its value from RGBPixels[0], [2] from [1], [n] from [n-1]. The pixels seem to be pulling a color from their parent just fine, but either the first pixel in the chain isn't querying the colorGenerator properly, or the colorGenerator isn't updating properly.
To update the colorGenerator, a colorController class oversees the whole process:
colorController.h:
#ifndef _COLORCONTROLLER_H
#define _COLORCONTROLLER_H
#include <list>
#include "colorGenerator.h"
#include "RGBPixel.h"
#include "globals.h"
#include "Arduino.h"
unsigned long millis();
typedef std::list<colorGenerator> generatorList;
class colorController
{
public:
virtual bool refresh();
protected:
generatorList generators;
};
#endif //_COLORCONTROLLER_H
As you can see, the controller has a list of colorGenerators and method to refresh them all (called from loop()), which unless overridden in the child class, does this:
bool colorController::refresh()
{
for (generatorList::iterator it = generators.begin(); it != generators.end(); ++it)
it->refresh();
bool dirty = false;
for (int i = NUM_OF_LEDS-1; i >= 0; --i)
dirty |= RGBPixels[i].refresh();
return dirty;
}
The CC_Cascade class (derived from colorController) sets things up like this:
CC_Cascade.h
#ifndef _CC_CASCADE_H
#define _CC_CASCADE_H
#include "colorController.h"
class CC_Cascade : public colorController
{
public:
CC_Cascade();
~CC_Cascade();
};
#endif //_CC_CASCADE_H
CC_Cascade.cpp
#include "CC_Cascade.h"
#include "CG_EmeraldWaters.h"
CC_Cascade::CC_Cascade()
{
colorGenerator * freshBubblingSpring = new CG_EmeraldWaters();
generators.push_back(*freshBubblingSpring);
RGBPixels[0].setParent(freshBubblingSpring);
RGBPixels[0].setDelay(40);
for (int i = 1; i < NUM_OF_LEDS; ++i)
{
RGBPixels[i].setParent(&RGBPixels[i-1]);
RGBPixels[i].setDelay(500-(9*i)); //FIXME: magic number only works for 50ish pixels
}
}
CC_Cascade::~CC_Cascade()
{
//TODO: delete generators
}
So far so clear?
Let me draw your attention to the colorController::refresh() function. What should happen is that every time it's called, there's one colorGenerator in the generators list (because the CC_Cascade constructor put it there), which is a CG_EmeraldWaters. When refresh() is called on this (through the iterator), it calls colorGenerator::refresh(), which in turn calls updateColor(). In the case of CG_EmeraldWaters, this is overriden, so CG_EmeraldWaters::updateColor SHOULD be called, giving a turquoise colour. Using some serial write statements to debug, I can see that IN FACT colorGenerator::updateColor() is called, so in that case I'd expect an orangey colour, BUT neither of these is affecting the colour of the pixels, which are all staying a purple colour as set in the CG_EmeraldWaters contructor.
Doing a little messing about, I added the following line to colorGenerator::updateColor(): RGBPixels[0].setColor(255,127,0);
Rather than the orange colour I was hoping for, the first pixel alternated quickly between purple and orange, suggesting (IMHO) that my new line of code was doing its job, but then the pixel was pulling its original purple colour again from the colorGenerator, and that somehow colorGenerator::updateColor() doesn't change the colour of the colorGenerator (given that I don't get a compile error, what IS it changing?).
So my qustions are: (QUESTIONSHERE)
1) How can I change the value of colorSource::currentR(/G/B) from within colorGenerator::updateColor(), given that currentR(/G/B) is declared as protected in colorSource and that colorGenerator is directly derived from colorSource?
2) Given an instance of CG_EmeraldWaters, how can I call CG_EmeraldWaters::updateColor() via colorGenerator::refresh(), which CG_EmeraldWaters inherits, given that updateColor() is declared as virtual in colorGenerator and overriden in CG_EmeraldWaters?
Below is the code of colorGenerator and CG_EmeraldWaters:
colorSource.h:
#ifndef _COLORSOURCE_H
#define _COLORSOURCE_H
#include "Arduino.h"
#ifdef DEBUG
#include "colorGenerator.h" //FIXME: delete Me
#endif
//#define byte unsigned char
typedef byte colorStorage_t;
class colorSource
{
public:
colorSource();
colorSource(colorStorage_t initialR, colorStorage_t initialG, colorStorage_t initialB);
void setColor(colorStorage_t newR, colorStorage_t newG, colorStorage_t newB);
//TODO: better implementation than this
colorStorage_t getR();
colorStorage_t getG();
colorStorage_t getB();
bool hasChanged();
protected:
colorStorage_t currentR;
colorStorage_t currentG;
colorStorage_t currentB;
bool dirty;
#ifdef DEBUG
friend colorGenerator; //FIXME: delete Me
#endif
};
#endif //_COLORSOURCE_H
colorSource.cpp:
#include "colorSource.h"
colorSource::colorSource()
{
//nothing here
}
colorSource::colorSource(colorStorage_t initialR, colorStorage_t initialG, colorStorage_t initialB)
:
currentR(initialR),
currentG(initialG),
currentB(initialB)
{
//intialised in the list
Serial.println("Constructed Color Source with initial color");
}
void colorSource::setColor(colorStorage_t newR, colorStorage_t newG, colorStorage_t newB)
{
currentR = newR;
currentG = newG;
currentB = newB;
}
colorStorage_t colorSource::getR()
{
return currentR;
}
colorStorage_t colorSource::getG()
{
return currentG;
}
colorStorage_t colorSource::getB()
{
return currentB;
}
bool colorSource::hasChanged()
{
return !dirty;
}
colorGenerator.h:
#ifndef _COLORGENERATOR_H
#define _COLORGENERATOR_H
#include "colorSource.h"
#ifdef DEBUG
#include "RGBPixel.h" //delete me, used for debugging!
#include "globals.h" //and me!
#endif
extern "C" unsigned long millis();
class colorGenerator : public colorSource
{
public:
colorGenerator(colorStorage_t initialR, colorStorage_t initialG, colorStorage_t initialB);
bool refresh();
protected:
virtual void updateColor();
unsigned long nextColorUpdate = 0;
unsigned short delay = 40;
};
#endif //_COLORGENERATOR_H
colorGenerator.cpp:
#include "Arduino.h"
#include "colorGenerator.h"
colorGenerator::colorGenerator(colorStorage_t initialR, colorStorage_t initialG, colorStorage_t initialB)
:
colorSource(initialR,initialG,initialB)
{
//intialised in the list
//Serial.println("Constructed Color Generator");
}
bool colorGenerator::refresh()
{
#ifdef DEBUG
Serial.print("colorGenerator::refresh()");
#endif
if (millis() < nextColorUpdate)
return false;
nextColorUpdate = millis() + (unsigned long) delay;
this->updateColor();
return true;
}
void colorGenerator::updateColor() //this function gets called (even if it has been overridden in a child class), but the code in it doesn't have the desired effect
{
#ifdef DEBUG
//Serial.print("colorGenerator::updateColor()");
//RGBPixels[0].setColor(255,127,0);
#endif
currentR = random(127,255);
currentG = random(0,127);
currentB = 0;
}
CG_EmeraldWaters.h:
#ifndef _CG_EMERALDWATERS_H
#define _CG_EMERALDWATERS_H
#include "colorGenerator.h"
#include "globals.h"
#include "RGBPixel.h"
class CG_EmeraldWaters : public colorGenerator
{
public:
CG_EmeraldWaters();
protected:
void updateColor();
};
#endif //_CG_EMERALDWATERS_H
CG_EmeraldWaters.cpp:
#include "Arduino.h"
#include "CG_EmeraldWaters.h"
CG_EmeraldWaters::CG_EmeraldWaters()
:
colorGenerator(255,0,255) //this color seems to stick! Changes made by updateColor() aren't propogated to the pixels.
{
//initialised in list
//Serial.println("Constructed Emerald Waters");
}
long random(long,long);
void CG_EmeraldWaters::updateColor() //this never seems to be called!
{
currentR = 0;
currentG = random(0,255);
currentB = random(0,255);
}
And finally, the main sketch file:
#include "FastSPI_LED2.h"
#include <StandardCplusplus.h>
#include "colorController.h"
#include "RGBPixel.h"
#include "globals.h"
#include "CC_Cascade.h"
colorController * currentColorController;
RGBPixel RGBPixels[NUM_OF_LEDS];
struct CRGB ledString[NUM_OF_LEDS];
void setup()
{
#ifdef DEBUG
//debugging:
Serial.begin(9600);
Serial.println("In Setup");
#endif
// sanity check delay - allows reprogramming if accidently blowing power w/leds
//delay(2000);
LEDS.setBrightness(8);
LEDS.addLeds<WS2801>(ledString, NUM_OF_LEDS);
currentColorController = new CC_Cascade();
}
void writeValuesToString()
{
for (int i = 0; i < NUM_OF_LEDS; ++i)
ledString[i] = CRGB(RGBPixels[i].getR(),RGBPixels[i].getG(),RGBPixels[i].getB());
LEDS.show();
}
void loop()
{
static bool dirty = false; //indicates whether pixel values have changed since last hardware write
//unsigned long lastHardwareWrite = 0; //time of last hardware write - only do this once per milisecond to avoid flicker (this method doesn't work, still flickers)
dirty |= currentColorController->refresh();
if (dirty)
{
dirty = false;
writeValuesToString();
delay(1); //to prevent flicker
}
}
Your problem is due to the so-called object slicing. Here is what is going on: when you declare a list of type generatorList
typedef std::list<colorGenerator> generatorList;
its members are restricted to what is in colorGenerator. Nothing from the derived class matters, so when you push
colorGenerator * freshBubblingSpring = new CG_EmeraldWaters();
generators.push_back(*freshBubblingSpring);
the CG_EmeraldWaters part that is not also in colorGenerator gets "sliced off"; you end up with a version of colorGenerator.
The reason for this is described in the wikipedia article linked above. To fix this problem, change the list to contain pointers, preferably smart pointers, pointing to colorGenerator instances. Then the slicing problem will no longer be relevant:
typedef std::list<unique_ptr<colorGenerator> > generatorList;
...
unique_ptr<colorGenerator> freshBubblingSpring(new CG_EmeraldWaters());
generators.push_back(freshBubblingSpring);
You should be able to call the base class's private and protected methods from the derived class, unless I'm missing something.
To call an overidden method (e.g. virtual foo() is defined in class Base and overridden in class Derived), you can access the Base method by calling derivedObj.Base::foo() in your code.

c++ timer implemetation - Assigning a member function to signal callback function pointer

hi i have been tryng to implement a timer using posix timer libs, but i am making a mistake in the implemetation, i was using a example from the web and tryng to encapsulating in a class but, the compiler doesnt like it, basically tryng to asign the callback function int the sigev.sigev_notify_function = TIMER0_IRQHandler; but i cannot get any result.
Here goes the code:
The class definition:
#include <sys/time.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
//se agrego para eliminar el siguiente warning del compilador
//warning: incompatible implicit declaration of built-in function 'memset'
#include <string.h> /* memset */
#include <unistd.h> /* close */
#define TIMEVAL_MAX 0xFFFFFFFF
#define TIMEVAL unsigned int
// The timer is incrementing every 4 us.
//#define MS_TO_TIMEVAL(ms) (ms * 250)
//#define US_TO_TIMEVAL(us) (us>>2)
// The timer is incrementing every 8 us.
#define MS_TO_TIMEVAL(ms) ((ms) * 125)
#define US_TO_TIMEVAL(us) ((us)>>3)
class Timer
{
public:
Timer();
void initTimer();
void setTimer(TIMEVAL aValue);
TIMEVAL getElapsedTime( void ) ;
void TIMER0_IRQHandler(sigval_t val);
private:
struct timeval last_sig;
timer_t timer;
};
and the function that is conflicting with the compiler:
void Timer::initTimer()
{
struct sigevent sigev;
// Take first absolute time ref.
if(gettimeofday(&last_sig,NULL)){
perror("gettimeofday()");
}
memset (&sigev, 0, sizeof (struct sigevent));
sigev.sigev_value.sival_int = 0;
sigev.sigev_notify = SIGEV_THREAD;
sigev.sigev_notify_attributes = NULL;
sigev.sigev_notify_function = &TIMER0_IRQHandler;
if( timer_create (CLOCK_REALTIME, &sigev, &timer)) {
perror("timer_create()");
}
}
*//callback function
void Timer::TIMER0_IRQHandler(sigval_t val)
{
if(gettimeofday(&last_sig,NULL)) {
perror("gettimeofday()");
}
printf("TIMER NOTIFY\n");
}
thx in advance!
To call a member function, you also need the pointer to this, which means you can't do this directly. You can, however, use a static function as a wrapper for your callback, which can extract the this pointer and call your real callback:
class Timer
{
public:
static void handler_wrapper(sigval_t val);
void handler();
};
void Timer::handler_wrapper(sigval_t val)
{
Timer *object = (Timer *)val.sival_ptr;
object->handler();
}
void Timer::handler(void)
{
// do whatever. just remember what thread context you're in
}
// in main
sigev.sigev_value.sival_ptr = (void*) this;
sigev.sigev_notify_function = &Timer::handler_wrapper;
You can use std::bind to create a function object to pass non-static member functions to be invoked at the end of timer(or timeout).
std::bind(&class_name::mem_func, obj_ptr, args_list ...);
The drawback with this is similar to static member functions, i.e. the thread does not have context of the parent object, although you might be able to run the member function stand-alone (if it doesn't use any parent object attributes). Which is pretty much the same as with static member function, which requires member attributes of the class to be static in case they are required/accessed/used by it.
Note: Pass the object reference (using std::ref) or pointer in arguments and use it in your member function.