Use volatile class members for interrupt handling - c++

Lets suppose it is embedded development of some ARM controller. Lets suppose we have some variable, which can be assigned from an interrupt or "mainThread" - (is it a "main loop" or RTOS thread). In C world volatile keyword should be used in this case and code may look like this:
/* Some subsystem .C file */
static volatile uint8_t state;
void on_main_thread(void) {
state = 1; /* Changing state in this context */
}
void on_interrupt(void) {
state = 0; /* Changing state from interrupt */
}
uint8_t get_state(void) {
return state; /* Getting the state in whatever context */
}
volatile keyword is essential in this situation. Now our company rewrites some code to C++ and the same subsystem example looks like this (I use enum here to emphasize the problem)
class SomeSubsystem
{
public:
enum class States
{
Off,
Idle,
Up,
Down,
};
States getState() const { return mState; }
void onMainThread(void) {
mState = States::Idle; // Changing state in this context
}
// Somehow this function is called from the interrupt
void onInterrupt(void) {
mState = States::Up; // Changing state from interrupt
}
private:
States mState; // <-- Here! Volatile?
//...
};
Now States mState should be volatile because it is shared among different contexts. But If one sets it as volatile... Then volatile works like plague for C++ class and one have to volatilize everything around. Like volatile enum class States, getState() volatile etc. Which doesn't look good for me (am I wrong?)
So. What is the right way to handle this situation in C++?
P.S. I would try to define "this situation" as: "possible usage of a class members from different contexts like interrupts and normal code execution"

This could work if you only need a single instance of SomeSubsystem in your program (which I assume as per the c code you posted.
If you need multiple instances, then maybe you could modify mState to be a States array or some similar structure instead.
class SomeSubsystem
{
public:
enum class States
{
Off,
Idle,
Up,
Down,
};
States getState() const { return mState; }
void onMainThread(void) {
mState = States::Idle; // Changing state in this context
}
// Somehow this function is called from the interrupt
void onInterrupt(void) {
mState = States::Up; // Changing state from interrupt
}
// Make mState public in order to access it from the rest of your code
// Otherwise, keep it private and create static set/get functions
public:
static volatile States mState; // <-- Here! Volatile?
//...
};
Then define mState somewhere (eg. in SomeSubsystem.cpp)
volatile SomeSubsystem::States SomeSubsystem::mState = SomeSubsystem::States::Off;
Now you are able to access mState from anywhere in your code like this
SomeSubsystem::mState = SomeSubsystem::States::Off;

I believe you only need to volatile-qualify the methods if the class object itself is volatile. However, I don't think you should have any issues if you just make the relevant member variables volatile. I have done this successfully (i.e. it compiled/ran) on something similar to what you are trying to achieve.. E.g.:
class SomeSubsystem
{
public:
...
void onMainThread(void); // no volatile qualification necessary
void onInterrupt(void); // "
private:
States volatile mState; // only make the relevant member variables volatile
}
SomeSubsystem aSubsystem; // don't make the object volatile
...
aSubsystem.onMainThread();
aSubsystem.onInterrupt();

Related

can i cast a template instance with arg uint to same template instance with arg int - is it compliant - to which standard?

Plz check following c++ code: (nothing special, should be compliant to c++ 2nd edition from 1991)
class C
{
// also defines all the methods called in template method below with the obvious name and args.
public: template<typename TEnum> void SetNullableEnumValuePtr(CNullable<TEnum>* aEnumPtr)
{
if(sizeof(TEnum) == 4)
{
this->SetNullableUInt32Ptr(reinterpret_cast<CNullable<UInt32>*>(aEnumPtr));
}
else if (sizeof(TEnum) == 2)
{
this->SetNullableUInt16Ptr(reinterpret_cast<CNullable<UInt16>*>(aEnumPtr));
}
else if (sizeof(TEnum) == 1)
{
this->SetNullableUInt8Ptr(reinterpret_cast<CNullable<UInt8>*>(aEnumPtr));
}
else
{
this->FailEnumSize();
}
}
}
basic conditions
class CNullable follows the well known, basic nullable pattern implemented i.e. for c# in certain frameworks but could also be any template with one argument.
as the names imply, TEnum is used for different enum types. i.e. enum FooEnum { foo };
For different platforms/compilers it is true, that with for the same enum type it goes to different if branches: On Win32 all enums are compiled with a size of 4 bytes always. On my MCU it compiles to uint8, if the values of the enum field implies to do so.
1. Not my focus:
i'm not sure, if the c++ standard allows compliant compilers to generate enums with different sizes in respect of the required size but i think it is said, that it is compliant.
2. not my focus:
it is also obvious that a cleaner / more robust solution would generate explicit template instances for the function for ALL used enum types. however i might do this and hope the compiler/optimizer is smart/compliant enough not to generate extra code for all types.
3. Not my focus:
i know that reinterpret_cast can be evil but there is a reason the c++ standard defines it. so please stop downrating my questions just because you dont like it or dont understand it.
4. Not my focus:
i know there are certain websites, you can test code on different compilers.
5. would help too:
also if you have a better idea how to treat this coding issue you're welcome.
6. My focus:
i wanna focus this question on the issue, if the c++ standard [1] (or any other standard) defines rule(s), that the templates instances for intXX and uintXX (Where XX is the bit width and u indicates a unsigned version of the native integer) must forcefully compile to a code where a reinterpret_cast from CNullable<unsigned intXX> to CNullable<intXX> is guaranteed to run properly. it seems to be likely that a straight forward implementation of the tool chain may not cause problems in this case, but i got used to be carefull with such assumptions.
conclusion:
if one could give me the reference(s) to chapter inside the (c++) or other standard treading these issues (1 to 6) - that would be great.
thank you!
[1] Please understand the sad reality, that the platform i work on does even not have a ready compiler specification. so it is quite trial and error and i wanna understand the techniques behind to get a better feeling for it - because treading the issue deterministic seems not to be possible since the compiler reference does not give a clear statement what standard it supports: They stated an ETSI standard rather than an iso c++ standard and they didnt give enough info to find the documentation of the standard - no version number, no document number, no document name. when i search for the info given in the compiler documentation on the ETSI page i get > 24.000 results (!) XD
PS: Edit: I request the info:
a) Which standards / specifications ensure that this code runs properly?
b) Does anyone have a clean solution for an abstraction using different template instances for different enum types?
Ok think i i have a clean solution. It uses a wrapper pattern to hide the abstraction of concrete value types behind a virtual method.
I hope that is enough code to get the intention. I also put a very simple example of how i use it.
I guess in newer c++ version there are some stl elements which can replace the classes i wrote. But i need it for a very old c++ standard, which even isn't iso.
I really hope there are no errors since not all symbols are defined. but the essential stuff shall be shown in this example implementation.
// platform dependent UInt32 type.
typedef unsigned __int32 UInt32;
template<typename T>
class CNullable
{
public: bool HasValue;
public: T Value;
public: CNullable(): Value(), HasValue(false) {}
public: CNullable(const T& aValue): Value(aValue), HasValue(true) {}
public: template<typename T1>
CNullable<T1> To() const
{
return this->HasValue ? CNullable<T1>(static_cast<T1>(this->Value)) : CNullable<T1>();
}
CNullable<UInt32> ToU32n() const
{
return this->To<UInt32>();
}
};
// U32-Nullable type
typedef CNullable<UInt32> UInt32n;
// U32-Nullable argument type.
typedef const CNullable<UInt32>& UInt32na;
// Some kind of variant value that can store different types of values pointing into an (intended) statically allocated model
class CValue
{
// Report failure. Break debuger, raise exception, log error - whatever.
private: static void Fail(const char* aTextPtr);
// Sets the value in the (intended) statically allocated datamodel
// Simple example with a UInt32 as new value
public: void SetTargetValue(UInt32na rhs);
// Sets the value in the (intended) statically allocated datamodel
// May use a visitor pattern (GOF) for resolving assigned types, but thats not the topic here.
public: void SetTargetValue(const CValue& rhs);
// Ensures that object is not sealed an fails if so.
private: bool CheckNotSealed();
// Allows to change to sealed state to protect the object agains modification.
// protection for "as static as possible"-memory-design
public: void Seal();
// Base class for Wrappers
class CValueWrapper
{
public: virtual ~CValueWrapper() {}
// Converts the current value as an U32.
public: virtual UInt32n GetInterpretedU32n() { Fail("Data conversion not supported."); return UInt32n(); }
// converts the new value from an U32 and sets the value
public: virtual void SetInterpretedU32n(UInt32na aU32n) { Fail("Data conversion not supported."); }
};
// Wrappers Base class for any enum related type.
class CEnumWrapperBase : public CValueWrapper
{
public: virtual UInt32n GetU32n() const = 0;
public: virtual void SetU32n(UInt32na aU32n) const = 0;
public: virtual UInt32n GetInterpretedU32n() { return this->GetU32n(); }
public: virtual void SetInterpretedU32n(UInt32na aU32n) { this->SetU32n(aU32n); }
};
// Wrapper base class for values of type = Nullable<TEnums>
template<class TEnum> class CNullableEnumWrapper : public CEnumWrapperBase
{
private: CNullable<TEnum>* mNullableEnumPtr;
public: CNullableEnumWrapper(CNullable<TEnum>* aNullableEnumPtr)
:
mNullableEnumPtr(aNullableEnumPtr)
{
}
public: virtual UInt32n GetU32n() const
{
return this->mNullableEnumPtr ? this->mNullableEnumPtr->ToU32n() : UInt32n();
}
public: virtual void SetU32n(UInt32na aU32n) const
{
if (this->mNullableEnumPtr)
{
*this->mNullableEnumPtr = aU32n.To<TEnum>();
}
}
};
// Wrapper base class for values of type = Nullable<TEnums>
template<class TEnum> class CEnumWrapper : public CEnumWrapperBase
{
public: CEnumWrapper(TEnum* aEnumPtr)
:
mEnumPtr(aEnumPtr)
{
}
private: TEnum* mEnumPtr;
public: virtual UInt32n GetU32n() const
{
return this->mEnumPtr ? static_cast<UInt32>(*this->mEnumPtr) : UInt32n();
}
public: virtual void SetU32n(UInt32na aU32n) const
{
if (this->mEnumPtr
&& aU32n.HasValue)
{
*this->mEnumPtr = static_cast<TEnum>(aU32n.Value);
}
}
};
// Allows to lock instantian of wrapper objects.
// In my bare metal application all wrappers are created on application startup
// and stay allocated until the device is switched of (by disconnecting power)
// [ThreadStatic]
public: static bool InstanciateValueWrapperEnabled;
// Set pointer to enum value (intended to be allocated in a static model)
public: template<class TEnum> void SetEnumValuePtr(TEnum* aEnumPtr)
{
if (this->InstanciateValueWrapperEnabled)
{
if (this->CheckNotSealed())
{
this->SetValueWrapperPtr(new CEnumWrapper<TEnum>(aEnumPtr), true);
}
}
else
{
Fail("Invalid operation.");
}
}
// Set pointer to nullable<enum> value (intended to be allocated in a static model)
public: template<class TEnum> void SetNullableEnumValuePtr(CNullable<TEnum>* aEnumPtr)
{
if (this->InstanciateValueWrapperEnabled)
{
if (this->CheckNotSealed())
{
this->SetValueWrapperPtr(new CNullableEnumWrapper<TEnum>(aEnumPtr), true);
}
}
else
{
Fail("Invalid operation.");
}
}
// Sets the member var and data type to 'CValueWrapper' (may support some types natively without a wrapper object)
public: void SetValueWrapperPtr(CValueWrapper* aValueWrapperPtr, bool aOwning);
};
// Model Base Code
//////////////////////////////////////
/// Application specific code
enum FooEnum { FooEnum_Val1 };
// Reads data from StdIn, uart, or whatever.
UInt32 ReadU32();
// Process data, for example output calculated data to another hardware interface.
void Process(CValue** aValuePtrs);
// Simple example of how its being used.
// in real environment its likely to encapsulate a set of CValue objects
// in a ModelInstance-Object and build it by a ModelDefinition object parsing a model definiton language (mdl)
// or adapt generated code. etc. etc...
void main()
{
// Define the static model:
static FooEnum gFooEnum;
static CNullable<FooEnum> gNullableFooEnum;
// Define the values to access the static model
CValue aFooEnumVal;
CValue aNullableFooEnumVal;
// Begin of init:
CValue::InstanciateValueWrapperEnabled = true;
aFooEnumVal.SetEnumValuePtr(&gFooEnum);
aNullableFooEnumVal.SetNullableEnumValuePtr(&gNullableFooEnum);
CValue::InstanciateValueWrapperEnabled = false;
// End of init
// Create an array of values
const UInt32 aPropertyCount = 2;
CValue* aPropertyPtrs[aPropertyCount] =
{
&aFooEnumVal,
&aNullableFooEnumVal
};
for (UInt32 aIdx = 0; aIdx < aPropertyCount; ++aIdx)
{
aPropertyPtrs[aIdx]->Seal();
}
// Very simple and unsave data receiption loop.
while (true)
{
UInt32 aPropertyIdToSet = ReadU32(); // The property id to receive.
UInt32 aNewValHasValue = ReadU32(); // Wether the value is defined
UInt32 aNewValValue = ReadU32(); // The value
UInt32n aNewVal // Nullable for NewValue
= aNewValHasValue // if value is defined
? UInt32n(aNewValValue) // Create a nullable which has a value
: UInt32n() // Create a nullable which has no value
;
CValue* aValuePtr = aPropertyPtrs[aPropertyIdToSet]; // Get the value to receive.
aValuePtr->SetTargetValue(aNewVal); // Set the value to the static model
Process(aPropertyPtrs); // Process data newly received.
}
}

How to declare a class member that may be one of two classes

I am working with a project that is largely not of my creation, but am tasked with adding in some functionality to it. Currently, there is a device class that has a member variable that is responsible for storing information about a storage location, setup like this:
device.hpp
class device {
public:
// Stuff
private:
// Stuff
StorageInfo storage_info_;
// Even more stuff
}
StorageInfo.hpp
class StorageInfo {
public:
void initializeStorage();
void updateStorageInfo();
int popLocation();
int peakLocation();
uint16_t totalSize();
uint16_t remainingSize();
// More declarations here
private:
//Even more stuff here
}
I am tasked with implementing a different storage option so that the two can be switched between. The information functions that this new storage option has would be the same as the initial storage option, but the implementation in retrieving that information is vastly different. In order to keep things clean and make it easier to maintain this application for years to come, they really need to be defined in two different files. However, this creates an issue inside of device.cpp, and in every single other file that calls the StorageInfo class. If I create two separate member variables, one for each type of storage, then not only will I need to insert a million different ifelse statements, but I have the potential to run into initialization issues in the constructors. What I would instead like to do is have one member variable that has the potential to hold either storage option class. Something like this:
StorageInfoA.hpp
class StorageInfoA: StorageInfo {
public:
void initializeStorage();
void updateStorageInfo();
int popLocation();
int peakLocation();
uint16_t totalSize();
uint16_t remainingSize();
// More declarations here
private:
//Even more stuff here
}
StorageInfoB.hpp
class StorageInfoB: StorageInfo {
public:
void initializeStorage();
void updateStorageInfo();
int popLocation();
int peakLocation();
uint16_t totalSize();
uint16_t remainingSize();
// More declarations here
private:
//Even more stuff here
}
device.hpp
class device {
public:
// Stuff
private:
// Stuff
StorageInfo storage_info_;
// Even more stuff
}
device.cpp
//Somewhere in the constructor of device.cpp
if(save_to_cache){
storage_info_ = StorageInfoA();
} else {
storage_info_ = StorageInfoB();
}
// Then, these types of calls would return the correct implementation without further ifelse calls
storage_info_.updateStorageInfo();
However, I know that cpp absolutely hates anything with dynamic typing, so I don't really know how to implement this. Is this kind of thing even possible? If not, does anyone know of a similar way to implement this that does work with cpp's typing rules?
You are on the right track, but you have to learn how to use polymorphism. In your example, you need the following fixes:
In the base class, make all functions virtual, and add a virtual
destructor:
class StorageInfo {
public:
virtual ~StorageInfo(){}
virtual void initializeStorage();
//...
};
Make your inheritance public:
class StorageInfoA: public StorageInfo {
Instead of holding StorageInfo by value, hold it in a smart pointer:
class device {
private:
std::unique_ptr<StorageInfo> storage_info_;
};
device constructor will look like
//Somewhere in the constructor of device.cpp
if(save_to_cache){
storage_info_ = std::make_unique<StorageInfoA>();
} else {
storage_info_ = std::make_unique<StorageInfoB>();
}
Finally, you will use it like an ordinary pointer:
storage_info_->updateStorageInfo();

Why is my FreeRTOS task not seeing the class members?

I'm trying to understand FreeRTOS building a C++ class which contains a LED blinking task. But in the task body (which is also a class member), other class members i.e. LED1_delay are empty/not-initialized. It seems like the task body was linked to another instance.
Class function which sets the blinking frequency and starts the task: (gpio.cpp)
void c_gpio::LED_blink_on(float frequency){
LED1_delay=(uint32_t)(1000/frequency);
if(LEDTaskcreated!=true){
//Create task
LEDTaskHandle = osThreadNew(startTask_LED1_blinker, LEDTask_args, &LEDTask_attributes);
LEDTaskcreated=true;
}
}
Wrapper function avoiding static declaration: (gpio.cpp)
void c_gpio::startTask_LED1_blinker(void* _this){
static_cast<c_gpio*>(_this)->taskbody_LED1_blinker((void*)0);
}
Task body: (gpio.cpp)
void c_gpio::taskbody_LED1_blinker(void* arguments){
//All class members are uninitialized here..
while(1)
{
HAL_GPIO_TogglePin(GP_LED_1_GPIO_Port,GP_LED_1_Pin);
osDelay(this->LED1_delay); //LED1_delay is not set.
}
}
Class declaration (gpio.hpp)
class c_gpio{
public:
void LED_blink_on(uint8_t LED_id, float frequency);
private:
static void startTask_LED1_blinker(void* _this);
void taskbody_LED1_blinker(void *arguments);
uint32_t LED1_delay;
//Task handles & attributes
osThreadId_t LEDTaskHandle;
osThreadAttr_t LEDTask_attributes;
uint16_t LEDTask_args[2];
};
Instantiation (main.cpp)
#include "gpio.hpp"
c_gpio gpio;
int main(void)
{
gpio.LED_blink_on(1,10);
/* Init scheduler */
osKernelInitialize();
/* Start scheduler */
osKernelStart();
}
I thought, the members taskbody_LED1_blinker() and LED1_delay are belonging to the same instance. But this doesn't seem to be so. Why? How to properly construct such a task?
Problem:
You have used the class object "gpio" for setting the blicking frequency. meaning the blinking frequency is updated inside the gpio object . Whereas "startTask_LED1_blinker" is a static method of a class which is not bound to any object
Solution:
Take advantage of the "arguments" to the task body function startTask_LED1_blinker. You can send 'this' pointer to the osThreadNew instead of LEDTask_args and then use that pointer to invoke task body "taskbody_LED1_blinker". Correction in your code for reference
void c_gpio::taskbody_LED1_blinker(void* arguments){
//All class members are uninitialized here..
while(1)
{
HAL_GPIO_TogglePin(GP_LED_1_GPIO_Port,GP_LED_1_Pin);
osDelay(LED1_delay); //LED1_delay is not set.
}
}
void c_gpio::LED_blink_on(float frequency){
LED1_delay=(uint32_t)(1000/frequency);
if(LEDTaskcreated!=true){
//Create task
LEDTaskHandle = osThreadNew(startTask_LED1_blinker, this, &LEDTask_attributes);
LEDTaskcreated=true;
}
}

What is the correct way of using C++ objects (and volatile) inside interrupt routines?

I am currently working with Atmel AVR microcontrollers (gcc), but would like the answer to apply to the microcontroller world in general, i.e. usually single-threaded but with interrupts.
I know how to use volatile in C code when accessing a variable that can be modified in an ISR. For example:
uint8_t g_pushIndex = 0;
volatile uint8_t g_popIndex = 0;
uint8_t g_values[QUEUE_SIZE];
void waitForEmptyQueue()
{
bool isQueueEmpty = false;
while (!isQueueEmpty)
{
// Disable interrupts to ensure atomic access.
cli();
isQueueEmpty = (g_pushIndex == g_popIndex);
sei();
}
}
ISR(USART_UDRE_vect) // some interrupt routine
{
// Interrupts are disabled here.
if (g_pushIndex == g_popIndex)
{
usart::stopTransfer();
}
else
{
uint8_t value = g_values[g_popIndex++];
g_popIndex &= MASK;
usart::transmit(value);
}
}
Because g_popIndex is modified inside the ISR and accessed outside of the ISR, it must be declared volatile to instruct the compiler not to optimize memory accesses to that variable. Note that, unless I'm mistaken, g_pushIndex and g_values need not be declared volatile, since they are not modified by the ISR.
I want to encapsulate the code related to the queue inside a class, so that it can be reused:
class Queue
{
public:
Queue()
: m_pushIndex(0)
, m_popIndex(0)
{
}
inline bool isEmpty() const
{
return (m_pushIndex == m_popIndex);
}
inline uint8_t pop()
{
uint8_t value = m_values[m_popIndex++];
m_popIndex &= MASK;
return value;
}
// other useful functions here...
private:
uint8_t m_pushIndex;
uint8_t m_popIndex;
uint8_t m_values[QUEUE_SIZE];
};
Queue g_queue;
void waitForEmptyQueue()
{
bool isQueueEmpty = false;
while (!isQueueEmpty)
{
// Disable interrupts to ensure atomic access.
cli();
isQueueEmpty = g_queue.isEmpty();
sei();
}
}
ISR(USART_UDRE_vect) // some interrupt routine
{
// Interrupts are disabled here.
if (g_queue.isEmpty())
{
usart::stopTransfer();
}
else
{
usart::transmit(g_queue.pop());
}
}
The code above is arguably more readable. However, what should be done about volatile in this case?
1) Is it still needed? Does calling the method Queue::isEmpty() somehow ensures non-optimized access to g_queue.m_popIndex, even if the function is declared inline? I doubt that. I know that compilers use heuristics to determine if an access should not be optimized, but I dislike relying on such heuristics as a general solution.
2) I think a working (and efficient) solution is to declare the member Queue::m_popIndex volatile inside the class definition. However, I dislike this solution, because the designer of class Queue needs to know exactly how it will be used to know which member variable must be volatile. It will not scale well with future code changes. Also, all Queue instances will now have a volatile member, even if some are not used inside an ISR.
3) If one looks at the Queue class as if it were a built-in, I think the natural solution would be to declare the global instance g_queue itself as volatile, since it is modified in the ISR and accessed outside of the ISR. However, this doesn't work well, because only volatile functions can be called on volatile objects. Suddenly, all member functions of Queue must be declared volatile (not just the const ones or the ones used inside the ISR). Again, how can the designer of Queue know that in advance? Also, this penalize all Queue users. There is still the possibility of duplicating all member functions and having both volatile and non-volatile overloads in the class, so that non-volatile users are not penalized. Not pretty.
4) The Queue class could be templated on a policy class that can optionally add volatile to all its member variables only when needed. Again, the class designer need to know that in advance and the solution is more complicated to understand, but oh well.
I am curious to know if I am missing some easier solution to this. As a side note, I am compiling with no C++11/14 support (yet).
Yes, inline is definetely needed.
1) Compilers generally places a new copy of the inlined function in each place it is called. This optimization seems to not affect volatile variables. So this is OK.
2) I second this as the correct solution(with an extension). Because your only variable that needs to be volatile is really queue index.
3) Nope, no need to mark whole class instance volatile since it may prevent other potential optimizations.
4) You can use inheritance. An interface that declares which functions must a queue have, and two inherited classes for one using with ISR(has queue index volatile) and the other for not using ISR. Additionally, you can always define your class also templated:
template<typename T>
class IQueue
{
public:
virtual bool isEmpty() const = 0;
virtual T pop() = 0;
protected:
uint8_t pushIndex;
T values[QUEUE_SIZE];
};
template<typename T>
class ISRQueue : public IQueue<T>
{
volatile uint8_t popIndex;
public:
inline bool isEmpty()const
{
return (pushIndex == popIndex);
}
inline T pop()
{
T value = values[popIndex++];
popIndex &= MASK;
return value;
}
};
template<typename T>
class Queue : public IQueue<T>
{
uint8_t popIndex;
public:
inline bool isEmpty()const
{
return (pushIndex == popIndex);
}
inline T pop()
{
T value = values[popIndex++];
popIndex &= MASK;
return value;
}
};
typedef ISRQueue<uint8_t> ISRQueueUInt;
typedef ISRQueue<uint8_t> QueueUInt;

How to change a behavior for all instances of a class in a header only class

For a class, which is only defined in a header, I need a special behavior of one method for all instance of the class. It should be depending on a default value, which can be changed any time during runtime. As I do not want a factory class nor a central management class I came up with that idea:
class MyClass
{
public:
void DoAnything() // Methode which should be act depending on default set.
{
// Do some stuff
if(getDefaultBehaviour())
{
// Do it this way...
}
else
{
// Do it that way...
}
}
static bool getDefaultBehaviour(bool bSetIt=false,bool bDefaultValue=false)
{
static bool bDefault=false;
if(bSetIt)
bDefault=bDefaultValue;
return bDefault;
}
};
It works, but it looks a little awkward. I wonder if there is a better way following the same intention.
In the case where I want to use it the software already created instances of that class during startup and delivered them to different parts of the code. Eventually the program gets the information how to treat the instances (for e.g. how or where to make themselves persistent). This decision should not only affect new created instances, it should affect the instances already created.
I'd advise to use a simple method to simulate a static data member, so the usage becomes more natural:
class MyClass
{
public:
// get a reference (!) to a static variable
static bool& DefaultBehaviour()
{
static bool b = false;
return b;
}
void DoAnything() // Methode which should be act depending on default set.
{
// Do some stuff
if(DefaultBehaviour())
{
// Do it this way...
}
else
{
// Do it that way...
}
}
};
where the user can change the default at any time with
MyClass::DefaultBehaviour() = true;
My thanks to Daniel Frey with his answer which I already marked as the best. I wanted to add my final solution which is based on the answer from Frey. The class is used by some c++ beginners. As I told them to use always getter and setter methods, the way described by Frey looks very complex to beginners ("uuuh, I can give a function a value?!?!"). So I wrote the class like followed:
class MyClass
{
public:
// get a reference (!) to a static variable
static bool& getDefaultBehaviour()
{
static bool b = false;
return b;
}
static void setDefaultBehaviour(bool value)
{
getDefaultBehaviour()=value;
}
void DoAnything() // Methode which should be act depending on default set.
{
// Do some stuff
if(getDefaultBehaviour())
{
// Do it this way...
}
else
{
// Do it that way...
}
}
};
for the user, I looks now like a usual getter and setter.