I have written code for a controller that has buttons and lamps. It is based on Arduino/ATmega2560. RAM is very limited. Changing to another device is no option.
Each button and lamp is modelled as a C++ class instance. They are derived from an abstract class Thing, which contains a pointer mpTarget that indicates connections between Things. The targets are initially assigned at runtime and do not change during runtime.
I need to save memory. As I have ~600 Things, it would help me alot if all those mpTargets were stored in ROM (and not in RAM), because the targets are known at compile time.
So I played with constexpr. However, it seems to be impossible to make a class only partially constexpr. I cannot make the whole class constexpr, because there are also member variables that change during runtime (mState in the example below).
What would be a good way to achieve this behavior? constexpr? Templates? Anything?
Clarifications
I know that mpTarget is currently initialized at runtime. But each target is known at compile time, that's why I'd like to find a good way to save RAM bytes of these pointers.
I don't really want to move away from object oriented design. I used an approach with simple, "plain old" datatypes before (which actually consumed less RAM). But as development of this code is still ongoing (classes and instances are added/deleted/modified), there is a high chance to miss necessary modifications, introducing hard-to-find bugs.
For this project, it would be more than enough to save the RAM storage only for mpTarget. Keeping the overhead of vtable pointers is fine. What I'm actually looking for is - as the title suggests - a way how we could implement a class whose members are partially constexpr. I also thought about using templates, but without success.
Sample code
#include <iostream>
class Thing
{
public:
// only called in setup()
void setTarget(Thing & pTarget)
{
mpTarget = & pTarget;
}
virtual void doSomething(int value) {};
protected:
// known at compile time - can we make this constexpr?
Thing * mpTarget;
};
class Button : public Thing
{
public:
void setState(int newState)
{
mState = mState + newState;
mpTarget->doSomething(mState);
}
private:
// changes during runtime
int mState;
};
class Lamp: public Thing
{
public:
virtual void doSomething(int value)
{
std::cout << value << std::endl;
};
};
Button myButton;
Lamp myLamp;
int main()
{
myButton.setTarget(myLamp);
while (1)
{
myButton.setState(123);
}
}
Redesign from ground up
ATmega2560 has just 8KB SRAM. This is extremely low compared to normal, desktop standards. 600 objects with 3 2-byte properties each would fill almost half of the available memory.
Programming on such a restrictive environment forces you to adapt your whole design from the start around the hardware limitations. Writing normal code and then trying to fit it to your hardware after the fact just doesn't cut it here. This is a good exception to the "first write readable code, then try it to optimize it".
One idea: group by properties, not by objects
First you need to abandon virtual methods. That would add at least a vtable pointer per instance. At 600 instances it is a very heavy cost.
One ideea I have for a design here is to ditch the OOP completely. Or at least partially. Instead of properties grouped per instance, group all properties together in vectors.
This has a few big advantages:
it saves space by abandoning the vtable
because how properties are grouped it makes it possible to store the properties known at compile time in ROM
it saves space by using the minimum number of bits necessary for each property
Example
For the sake of example let's consider this scenario:
we have 100 lamps, 200 buttons and 300 LEDs
buttons and LEDs have a target property. Only lamps can be targets
buttons have state property. There are 2 possible states (ON/OFF)
LEDs have color property. There are 16 possible predefined colors
The following examples use literals to be explicit, but in code you should use constants (e.g. NUM_BUTTONS etc.)
target property
We have 500 (200 buttons + 300 LEDS) objects with target property. So we need a vector of size 500. There are 100 targets. So the data type fits in a int8_t. So target looks like this:
constexpr int8t_t targets[500] = ...
In this vector the first 200 elements represent the targets of the buttons and the next 300 elements represent the targets of the LEDs. This vector will be stored in ROM.
To get the target of a thing we have:
int8_t button_target(int button) { return targets[button]; }
int8_t led_target(int led) { return targets[200 + led]; }
Alternatively use two vectors:
constexpr int8t_t button_targets[200] = ...
constexpr int8t_t led_targets[300] = ...
Populating this vector at compile time is a problem you need to solve. I don't know exactly how you are creating your objects now. You could hard code the values in the code or you could generate the code.
state property
We have 200 elements with state. Since there are 2 possible states we just need 1 bit per state. So we just need 200 / 8 = 25 bytes:
int8_t state[25] = {};
Getting and setting the state of a button is more complicated, it requires bitmask operations, but we have saved 175 bytes here (87.5% saved space on this data) and every byte matters.
bool button_get_state(int button)
{
int8_t byte = state[button / 8];
return byte & (1 << (button % 8));
}
color property
We have 300 elements with color. Since there are 16 colors we need 4 bits per color so we can encode 2 colors per byte:
int8_t color[150] = {};
Again getting and setting color requires bit fiddling.
Conclusion
As you can see this design is by far not as pretty as OOP. And is it requires more complex code. But it has the big advantage that is saves you a lot of space, which is paramount on a device with just 8,192 bytes of SRAM.
Use small state
Eliminate all virtual methods (and probably all base classes) to reduce class state.
Use Non-Type template parameters to move references from data (RAM) to code (ROM)
Use an enum based on a char for state when possible:
enum binary_enable_state : char {
BINARY_ENABLE_ON,
BINARY_ENABLE_OFF
};
Event listeners should just listen, and have no pointers or references or any real state.
class Lamp {
public:
void doSomething(binary_enable_state value)
{
std::cout << value << std::endl;
};
};
Event sources should uses non-type template parameter references to the listeners, thus avoiding pointers and state.
template<class Listener, Listener& mpTarget>
class Button {
public:
void setState(binary_enable_state newState)
{
mState = newState;
mpTarget.doSomething(mState);
}
private:
// changes during runtime
binary_enable_state mState; //Do you actually need to store this?
};
This basically puts the graph in the code itself (ROM), rather than as data in RAM.
Lamp myLamp;
Button<decltype(myLamp), myLamp> myButton;
int main()
{
myButton.setState(BINARY_ENABLE_ON);
}
http://coliru.stacked-crooked.com/a/7171e4b9547ccbe1
Related
I am solving the following problem. I am working on an optimization program in C ++ which, depending on the initial settings of the user, uses various regulations (standards) to calculate the target function. Suppose we have a method A based on some norm and a method B based on another norm to calculate the target function. The user is setting the right standard before starting the program. The rest of the code is the same. During optimization, the target function is iteratively called over and over again. Of course, there is a simple solution: each time the target function is called, the IF condition is used to decide which standard to use. But because the program has to make decisions in every iteration, it seems to be ineffective. The second option is to create 2 independent codes and run only the one with the required standard. This, in turn, is ugly in terms of duplicate code.
I imagined that I would create 2 different classes and use the selected class using the IF condition when constructing the object. This would make the program decide only once when creating the object, but during the iteration itself the object would be clearly defined. Unfortunately, this does not work because objects cannot be created in IF conditions.
//-----------------------------------------------------------
// Create object sensor based on input
if(data.sensors_tipe == "Uniaxial_025") Sensor_Uniaxial_025 sensor(data);
else if (data.sensors_tipe == "T_rosette_05") Sensor_T_rosette_05 sensor(data);
else report.error("some error");
// rotation test
int element_index = 1;
double orientation_angle = 3.490658503988659;
sensor.rotate(element_index, orientation_angle);
Another way I would like is to set the correct method using a parameter in the constructor. Unfortunately, that probably isn't possible either.
I am a beginner and I did not find the answer anywhere. So maybe someone can help. Thanks
This is a good job for templates, which are "recipes" to generate code.
The end result will be duplicated machine code, but without the duplication in the source.
template<typename MethodT>
float optimize(const MethodT& method) {
float v = method();
// etc...
}
float methodA();
float methodB();
int main() {
auto a = optimize(methodA);
auto b = optimize(methodB);
}
First, the solution with if may be not that bad. It is branch on each function call, but the branch should be predicted well.
Second, if the functions that implement method A and method B are large enough to miss inlining, use function pointer.
Otherwise, use static polymorphism with templates, method A and method B may be passed via template parameter as functors.
In case, the user can change standard after programm compilation (for example, before each run) you can create interface and 2 child from it.
So, at startup you should create the instance (one of 2) you need through new. And then you can use it.
You can't use that algorithm with stack instances.
One way is to use inheritance.
class Sensor
{
public:
virtual void rotate(int, double) = 0;
};
class Sensor_Uniaxial_025 : public Sensor
{
public:
virtual void rotate(int, double) {/*stuff*/};
};
class Sensor_T_rosette_05 : public Sensor
{
public:
virtual void rotate(int, double) {/*stuff*/};
};
Sensor* sensorToUse;
//-----------------------------------------------------------
// Create object sensor based on input
if(data.sensors_tipe == "Uniaxial_025") sensorToUse = new Sensor_Uniaxial_025(data);
else if (data.sensors_tipe == "T_rosette_05") sensorToUse = new
Sensor_T_rosette_05(data);
else report.error("some error");
// rotation test
int element_index = 1;
double orientation_angle = 3.490658503988659;
sensorToUse->rotate(element_index, orientation_angle);
The example above, with new, comes with serious memory management issues. But if you pre-allocate the sensor for each type, in a single instance, and use a look-up instead it works well.
The alternative is with template. See other answers for these approaches.
Context:
I am writing a specific communication protocol to be used between TLM models (HW blocks described with SystemC and thus C++).
TLM notion is not important, just note that this communication is mimicked by allocating objects, the generic payloads (gps), that are passed between these C++ models of HW blocks.
Aim:
Together with the protocol, I want to provide a memory manager that should be able to efficiently handle the gps; this is quite important since in one simulation lots of gps are constructed, used and destroyed and this can slow down things a lot.
My goal is also to create something simple that could be used by others without efforts.
Issues:
The first issue I had was in creating a single shared pool for all the blocks communicating with that protocol. I thought about creating a static member in the mm class, but then I realized that:
Static members require a definition in the cpp. This makes the mm class less intuitive to use (with different people using this, some will forget to do so) and I would prefer to avoid that.
Depending on where (and in which?) in the cpp file the static variable definition is done, the pool might not have wet the parameters needed to be initialized (i.e., the number of mm instances created).
The second issue is similar to the first one. I want to count the number of instances and thus instead of a pool I need to create a shared counter to be used then by the pool to initialize itself. Again, I wanted to avoid static variable definitions in a cpp file and to guarantee the order of initialization.
I have considered mainly:
static members (discarded for the reasons above)
Singletons (discarded because I don't need to create a whole class for the pool to make it visible by others and single-instanced)
static methods (the approaches I finally picked and that is not far from a complete Singleton)
This is the code I produced (only relevant part included):
/**
* Helper class to count another class' number of instances.
*/
class counter {
public:
// Constructor
counter() : count(0) {}
//Destructor
virtual ~counter() {}
private:
unsigned int count;
public:
unsigned int get_count() {return count;}
void incr_count() {count++;}
void decr_count() {count--;}
};
template <unsigned int MAX = 1>
class mm: public tlm::tlm_mm_interface {
//////////////////////////////TYPEDEFS AND ENUMS/////////////////////////////
public:
typedef tlm::tlm_generic_payload gp_t;
///////////////////////////CLASS (CON/DE)STRUCTOR////////////////////////////
public:
// Constructor
mm() {inst_count().incr_count();}
// Copy constructor
mm(const mm&) {inst_count().incr_count();}
// Destructor
virtual ~mm() {} // no need to decrease instance count in our case
////////////////////////////////CLASS METHODS////////////////////////////////
public:
// Counter for number of isntances.
static counter& inst_count() {
static counter cnt;
return cnt;
}
/* This pattern makes sure that:
-- 1. The pool is created only when the first alloc appears
-- 2. All instances of mm have been already created (known instance sequence)
-- 3. Only one pool exists */
static boost::object_pool<gp_t>& get_pool() {
static boost::object_pool<gp_t> p(
mm<MAX>::inst_count().get_count() * MAX / 2, // creation size
mm<MAX>::inst_count().get_count() * MAX // max size used
);
return p;
}
// Allocate
virtual gp_t* allocate() {
//...
return gp;
}
// Free the generic payload and data_ptr
virtual void free(gp_t* gp) {
//...
get_pool().destroy(gp);
}
}
Now, the initiator block class header should have a member:
mm m_mm;
And the initiator block class cpp should use this like:
tlm_generic_payload* gp;
gp = m_mm.allocate();
//...
m_mm.free(gp); // In truth this is called by gp->release()...
// ...not important here
Having an electronic HW background, I am mainly trying to improve coding style, learn new approaches and optimize speed/memory allocation.
Is there a better way to achieve this? In particular considering my doubts:
It seems to me a not optimal workaround to encapsulate the counter in a class, put it locally (but static) in a static method and then do the same for the pool.
even though SystemC "simulation kernel" is single-threaded, I need to consider a multithread case...I am not sure that the relationship between those two static methods is safe even thou independently they should be safe...with C++03 g++ adds code to guarantee it and with C++11:
ยง6.7 [stmt.dcl] p4 If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
Thanks in advance.
Would this general concept be considered "bad"? The concept of using function typedefs for precalculating which function's are better optimized to handle the data stored? Or should I just stick to if and switch statements to keep other programmers from cringing? Aside from cringing at the names in this throw together example:
#ifndef __PROJECT_CIMAGE_H_
#define __PROJECT_CIMAGE_H_
#define FORMAT_RGB 0
#define FORMAT_BGR 1
typedef unsigned char ImageFormat;
class CImage
{
protected:
// image data
Components* data;
ImageFormat format;
// typedef the functions
typedef void(*lpfnDeleteRedComponentProc)();
typedef void(*lpfnDeleteGreenComponentProc)();
typedef void(*lpfnDeleteBlueComponentProc)();
// specify the different functions for each supported format
void DeleteRedComponentRGB();
void DeleteGreenComponentRGB();
void DeleteBlueComponentRGB();
void DeleteRedComponentBGR();
void DeleteGreenComponentBGR();
void DeleteBlueComponentBGR();
// Add in references to which functions to use.
lpfnDeleteRedComponentProc DRC;
lpfnDeleteGreenComponentProc DGC;
lpfnDeleteBlueComponentProc DBC;
public:
Image(); // Allocate some basic data
~Image(); // Deallocate stored data
// change the image format
void SetImageFormat(ImageFormat format)
{
// shift through the data and adjust it as neccissary.
switch (format)
{
case FORMAT_RGB:
// use functions specially suited for the RGB format
DRC = DeleteRedComponentRGB;
DGC = DeleteGreenComponentRGB;
DBC = DeleteBlueComponentRGB;
break;
case FORMAT_BGR:
// use functions specially suited for the BGR format
DRC = DeleteRedComponentBGR;
DGC = DeleteGreenComponentBGR;
DBC = DeleteBlueComponentBGR;
break;
}
}
// Set's the specifyed component to 0 throughout the entire image
void DeleteRedComponent() { DRC(); }
void DeleteGreenComponent() { DGC(); }
void DeleteBlueComponent() { DBC(); }
// more, similarly pourposed, functions here...
};
#endif // __PROJECT_CIMAGE_H_
There are many problems with the above code.
You use #define uselessly, and typedef where you should have an enum
enum class ImageFormat:unsigned char { // unsigned char optional
FORMAT_RGB, // =0 optional
FORMAT_BGR // =1 optional
};
Second, you have a cluster of virtual behavior you want to swap out in a clump. How does this not scream interface class to you?
struct ChannelSpecific {
virtual void DeleteGreen( CImage* ) = 0;
virtual void DeleteBlue( CImage* ) = 0;
virtual void DeleteRed( CImage* ) = 0;
// etc
};
template< ImageFormat format >
struct ChannelSpecificImpl;
template<>
struct ChannelSpecificImpl<FORMAT_RGB>:ChannelSpecific {
void DeleteGreen( CImage* ) final { /* etc...*/ }
// etc...
};
template<>
struct ChannelSpecificImpl<FORMAT_BGR>:ChannelSpecific {
// etc...
};
The overhead to calling the above virtual functions is marginally higher than a function pointer (due to the vtable being less likely to be in the cache), but in cases where you are doing a whole pile of operations in a row you can find the format and explicitly cast the worker and call the final methods with no function-pointer or virtual table overhead (up to and including allowing the methods to be inlined).
As a second advantage, a whole pile of the operations you want to perform on channels ends up being exceedingly uniform, and just a matter of what the offset of each channel is. So I can do away with the two above specializations by simply doing this:
enum class Channel { Red, Green, Blue };
template<ImageFormat, Channel> struct channel_traits;
template<> struct channel_traits<FORMAT_RGB, Red>:std::integral_constant< size_t, 0 > {};
template<> struct channel_traits<FORMAT_RGB, Green>:std::integral_constant< size_t, 1 > {};
template<> struct channel_traits<FORMAT_RGB, Blue>:std::integral_constant< size_t, 2 > {};
template<> struct channel_traits<FORMAT_BGR, Red>:std::integral_constant< size_t, 2 > {};
template<> struct channel_traits<FORMAT_BGR, Green>:std::integral_constant< size_t, 1 > {};
template<> struct channel_traits<FORMAT_BGR, Blue>:std::integral_constant< size_t, 0 > {};
and now I get to write my ChannelSpecificImpl<ImageFormat> without specializations -- I just need to access the above traits classes, and I get to write my code once, and use it multiple times.
Inside CImage I store a single ChannelSpecific pointer, which holds no data, just algorithms. When I swap out the image format, the ChannelSpecific pointer is swapped out. If I find I have a bottleneck in how I'm using ChannelSpecific because of too much vtable overhead, I get to refactor and put a mega-function in it.
If I hate the fact that I'm passing in the CImage all the time, I can give ChannelSpecific state of a pointer to the CImage internally, and now the code gets to use this->cimage to access the CImage.
On the other hand, code like what you wrote above has its place. I'd consider it better than massive case switch statements.
Note that a bit of the above code is C++11 specific (enum class, enum with storage specifier, final), but if you drop those features the solution is still viable.
Also note that your switch statement ends up looking like:
switch (format) {
case FORMAT_RGB:
channelSpecific.reset(new ChannelSpecificImpl<FORMAT_RGB>());
case FORMAT_BGR:
channelSpecific.reset(new ChannelSpecificImpl<FORMAT_BGR>());
which is far less to maintain and less likely to contain bugs. If you hate the free store (and more specifically, have found that format changes are common enough that the ::new call is a performance hit that matters), create a boost::variant or a C++11 union of each of the possible ChannelSpecificImpl. (std::unique_ptr<ChannelSpecific> channelSpecific, or std::shared_ptr, depending on various things -- use unique_ptr by default.)
Finally, if you get tired of maintaining that switch statement (as I tend to), making a cascading if based magic switch via template metaprogramming isn't that hard -- or even an array of function pointer factories that produce a ChannelSpecific* and an explicit array lookup to call one of them. (sadly, there isn't a variardic template expansion that produces an actual switch statement, but compilers might optimize chained sequential ifs into the same structure anyhow).
If you get nothing from the above code, the important part is that you don't want to hand write each of the zero functions. You want to not repeat yourself, write it once, factor out the differences between the foramts into a traits class, and have template functions on the format and channel produce the function that does the work and be written once. If you don't do this, you will either have to generate your code via macros and have an undebuggable mess, generate your code via some other method (and not be able to debug the generator, just the generated code), or you will have some corner case bug that occur only when doing some specific operation to some specific channel that your QA will miss. Maybe not today, maybe not tomorrow, but someday when a change is made and someone screws up the update to the 18th format specific function but only in the blue channel.
I'm in the midst of attacking an old per-channel imaging library that was done in this "virtual C-style function pointer swap out" pretty much exactly as you are proposing, and each function I touch gets rewritten using the above technique. I am reducing the amount of code by huge swaths, increasing reliability, and sometimes even getting performance boosts. Why? Because I was able to check common assumptions -- pixelstride equal to pixel packing, pixelstride in source and dest equal -- and generate a less-branchy version for that case, and fall back to a more-branchy for the corner cases, then apply that to a whole myriad of different pixel iteration code in one fell swoop. Maintaining N different pixel iterating code with that kind of micro optimization on top of the existing micro optimizations would be expensive: doing it this way means that I get to write it once, and reap the benefits N fold.
Typedefs are a good way to make the code much more readable. If you have a problem with the typedefs then it means it just does not contribute to the code's readability. Just changing the typedef name will solve the problem but you would need to change it everywhere in the existing codebase.
#Yakk comments on using virtual instead of function pointers are on the money; as well as the better solution also offered.
Given the reservations on the design here, its worth noting that:
// typedef the functions
typedef void(*lpfnDeleteRedComponentProc)();
typedef void(*lpfnDeleteGreenComponentProc)();
typedef void(*lpfnDeleteBlueComponentProc)();
creates distinct new type names for each component, even though they have the same signature. If I were going down this path I'd have a single type name which would make clear the expected common behavior.
I'm a new programmer trying to teach myself C++ and OOP. Right now I'm working on a project with multiple states, and it's my first attempt at a bigger programming task/challenge. Right now I'm trying to work on a simple menu state class, but I'm not sure how to go about designing it to do what I want. Specifically, I'm a beginner programmer, and I'm not sure what data types or templates to use.
Here's a brief description of the type of menu screen that I want to make:
I'm trying to make a simple menu screen that has multiple 'menu elements' listed. Each 'menu element' contains a string (which is used to display the name of the element or item) and a function (to change states, screens, do a task, etc.). -- The other 'feature' of the menu system that I want to have is the ability for selection to wrap around. For example, if the menu has 3 items, I want the user to be able to press up and down to switch the currently selected element, and if the user is on the last element (#3) and presses down again, they wrap back around to element #1. (and vice versa..)
Now, I ~think~ that this will require me to make a new class called 'MenuElement', and a class called 'MenuState' which contains and allows the user to switch between and select a specific element.
As a new, self-taught C++ programmer, I have limited experience with some of the more advanced containers and I'm much more familiar with simple c-style arrays. I have a little bit of experience with vectors, and I know a little bit about linked lists, maps, and trees, but I'm not sure which container type to use for this situation. I've seen examples of using pointers to create a circular linked list before, but the example was in C and it seemed a little bit clumsy. Is there a specific type of contain that bests fits my problem? Or am I over-thinking it, and I should just pick one and run with it?
I think I could eventually get this system up and running using an array of MenuElements with a series of conditional statements. However, I'm really interested in actually learning how to improve my programming and design, not only so that my code runs faster, but also so that my code is clean and logically constructed.
Wrap-around can be done with modulo arithmetic, i.e. given a private member index variable named idx_ of the active cursor into the array of menu elements, you could do have a member functions page_down() and page_up() like this
void CompositeMenu::page_down()
{
// idx_ will always remain in the interval [0, num_elements() )
// when idx_ equals num_elements() - 1, page_down() will yield 0 (i.e. wrap-around)
idx_ = (idx_ + 1) % num_elements();
}
void CompositeMenu::page_up()
{
// idx_ will always remain in the interval [0, num_elements() )
// when idx_ equals 0, page_up() will yield num_elements() - 1 (i.e. wrap-around)
idx_ = (idx_ - 1 + num_elements() ) % num_elements() ;
}
where num_elements() is the size of the current menu.
For your general design question, a sketch of your class hierarchy would use the Composite Design Pattern (Wikipedia). This defines an abstract menu interface called IMenu and derives both CompositeMenu and a LeafMenu as concrete classes. This allows arbitrary levels of nested submenus.
class IMenu
{
public:
virtual void SomeOperation() = 0;
};
class CompositeMenu
:
public IMenu
{
public:
virtual void SomeOperation()
{
// your implementation
}
void page_down(); // use implementation above
void page_up(); // use implementation above
int num_elements { return subMenus_.size(); }
private:
std::vector<IMenu*> subMenus_; // list of subMenus, which can also have nested menus themselves
int idx_; // active menu element
};
class LeafMenu
:
public IMenu
{
public:
virtual void SomeOperation()
{
// your implementation
}
};
Note that since num_elements() is a member function that allows for dynamic updates on the subMenus (i.e. it would allow drag-and-drop of menu items).
I recently wrote a class that renders B-spline curves. These curves are defined by a number of control points. Originally, I had intended to use eight control points, so I added a constant to the class, like so:
class Curve
{
public:
static const int CONTROL_POINT_COUNT = 8;
};
Now I want to extend this class to allow an arbitrary amount of control points. So I want to change this to:
class Curve
{
public:
int getControlPointCount() {return _controlPointCount;}
};
The question is whether it isn't better to store constants in methods to begin with, to facilitate adaptability. In other words, isn't it better to have started thus:
class Curve
{
public:
int getControlPointCount() {return 8;}
};
The advantage of this is that I could have just changed one symbol in the method in question, instead of moving around constants etc.
Is this a good practice or a bad one?
int getControlPointCount() {return _controlPointCount;}
This is an accessor. Swapping a const static for an accessor is not really a gain as litb has pointed out. What you really need to future-proof is probably a pair of accessor and mutator.
int getControlPointCount() {return _controlPointCount;} // accessor
I'd also throw in a design-const for the accessor and make it:
int getControlPointCount() const {return _controlPointCount;} // accessor
and the corresponding:
void setControlPointCount(int cpc) { _controlPointCount = cpc;} //mutator
Now, the big difference with a static object is that the control-point count is no longer a class-level attribute but an instance level one. This is a design change. Do you want it this way?
Nit: Your class level static count is public and hence does not need an accessor.
Typically I favour maintaining as few couplings manually as possible.
The number of control points in the curve is, well, the number of control points in the curve. It's not an independent variable that can be set at will.
So I usually would expose a const standard container reference:
class Curve
{
private:
std::vector<Point>& _controlPoints;
public:
Curve ( const std::vector<Point>& controlPoints) : _controlPoints(controlPoints)
{
}
const std::vector<Point>& getControlPoints ()
{
return _controlPoints;
}
};
And if you want to know how many control points, then use curve.getControlPoints().size(). I'd suspect that in most of the use cases you'd want the points as well as the count anyway, and by exposing a standard container you can use the standard library's iterator idioms and built-in algorithms, rather getting the count and calling a function like getControlPointWithIndex in a loop.
If there really is nothing else in the curve class, I might even go as far as:
typedef std::vector<Point> Curve;
(often a curve won't render itself, as a renderer class can have details about the rendering pipeline, leaving a curve as purely the geometric artifact)
To better answer your question, one should also know how the controlPointCount variable is set. Is it set outside from your class? In this case, you should also define a setter. Or the Curve class is the sole responsible for setting it? Is it set only on compile time or also on runtime.
Anyway, avoid a magic number even in this form:
int getControlPointCount() {return 8;}
This is better:
int getControlPointCount() {return CONTROL_POINT_COUNT;}
A method has the advantage that you can modify the internal implementation (use a constant value, read from a configuration file, alter the value dynamically), without affecting the external of the class.
class Curve
{
private:
int _controlPointCount;
void setControlPointCount(int cpc_arg)
{
_controlPointCount = cpc_arg;
}
public:
curve()
{
_controlPointCount = 8;
}
int getControlPointCount() const
{
return _controlPointCount;
}
};
I will create a code like this, with set function in private, so that no body can play with control point count, until we move to the next phase of development..where we update start to update the control point count at runtime. at that time, we can move this set method from private to public scope.
While understanding the question, I have a number of conceptual problems with the example:
What is the return value for getControlPointCount() when the number of control points is not limited?
Is it MAXINT?
Is it the current number of control points on the curve (thus breaking the logic that says that this is the largest possible number of points?)
What happens when you actually attempt to create a curve with MAXINT points? You will run out of memory eventually.
The interface itself seems problematic to me. Like other standard collection classes, the class should have encapsulated its limitation on number of points, and its AddControlPoint() should have returned an error if a limitation on size, memory, or any other violation has occurred.
As for the specific answer, I agree with kgiannakakis: a member function allows more flexibility.
I tend to use configuration + constant (default value) for all 'stable' values through the execution of the program. With plain constants for values that cannot change (360 degrees -> 2 pi radians, 60 seconds -> 1 minute) or whose change would break the running code (minimum/maximum values for algorithms that make them unstable).
You are dealing with some different design issues. First you must know whether the number of control points is a class or instance level value. Then whether it is a constant at any of the two levels.
If all curves must share the same number of control points in your application then it is a class level (static) value. If different curves can have different number of control points then it is not a class level value, but rather a instance level one.
In this case, if the number of control points will be constant during the whole life of the curve then it is a instance level constant, if it can change then it is not constant at this level either.
// Assuming that different curves can have different
// number of control points, but that the value cannot
// change dynamically for a curve.
class Curve
{
public:
explicit Curve( int control_points )
: control_points_( control_points )
{}
// ...
private:
const int control_points_;
};
namespace constant
{
const int spline_control_points = 8;
}
class Config
{
public:
Config();
void readFile( std::string const & file );
// returns the configured value for SplineControlPoints or
// constant::spline_control_points if the option does not
// appear in config.
int getSplineControlPoints() const;
};
int main()
{
Config config;
config.readFile( "~/.configuration" ); // read config
Curve c( config.getSplineControlPoints() );
}
For integral type I'm usualy using:
class Curve
{
public:
enum
{
CONTROL_POINT_COUNT = 8
};
};
If constant doesn't need for any entities except class implementation I declare constants in *.cpp file.
namespace
{
const int CONTROL_POINT_COUNT = 8;
}
In general, all your data should be private and accessed via getters and setters. Otherwise you violate encapsulation. Namely, if you expose the underlying data you lock yourself and your class into a particular representation of that underlying data.
In this specific case I would have done the something like the following I think:
class Curve
{
protected:
int getControlPointCount() {return _controlPointCount;}
int setControlPointCount(int c) { _controlPointCount = c; }
private:
static int _controlPointCount = 0;
};
Constants in general should not be defined inside methods. The example you're choosing has two unique features. First, it's a getter; second, the type being returned is an int. But the point of defining constants is to use them more than once, and to be able to refer to them in a convenient way. Typing "8" as opposed to "controlPointCount" may save you time, and may not seem to incur a maintenance cost, but this won't typically be true if you always define constants inside methods.