Related
This is probably very basic but somehow I cannot figure it out.
Say I have a class A which embeds 42 Things, plus some common data:
class A {
Thing things[42];
int common_data[1024];
}
I would like each thing to have access to the common data, but I don't want to copy the data in each Thing object, nor pay the price of a pointer to it in each thing. In other word, I would like Thing to look like this:
class Thing {
int ident;
int f() {
return common_data[ident];
}
}
Of course here common_data is unbound. What is the canonical way to make this work?
FWIW I am working with a subset of C++ with no dynamic allocation (no "new", no inheritance, basically it's C with the nice syntax to call methods and declare objects); I am ideally looking for a solution that fits in this subset.
You can solve your issue by making the common_data attribute of Class A static. Static variables are shared by all members of class A, and will be accessible if you make it public.
class A
{
private:
Thing things[42];
public:
static int common_data[1024];
}
It can be accessed by doing...
A::common_data[index];
I am not sure if I understand the question correctly, but maybe this helps:
struct A {
Thing things[42];
int common_data[1024];
void foo(int index) {
things[index].doSomeThingWithCommonData(int* common_data);
}
};
struct Thing {
void doSomeThinWithCommonData(int* common_data) {
/* now you have access to common_data */
}
};
Your reasons for avoiding pointers/reference is based on irrational fears. "Copying" a pointer 42 times is nothing (read this word carefully) for the machine. Moreover this is definitely not the bottleneck of the application.
So the idiomatic way is to simply use dependency injection, which is indeed a slightly more costly action for you (if passing an array can be considered costly), but allows for a much more decoupled design.
This is therefore the solution I recommend:
struct Thing {
using data = std::shared_ptr<std::array<int, 1024>>;
data common_data;
Thing(data arg)
: common_data(arg)
{}
// ...
};
If the system is costrained, then you should benchmark your program. I can tell you already with almost absolutely certainty that the bottleneck won't be the copying of those 42 pointers.
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 have the following struct which will be used to hold plugin information. I am very sure this will change (added to most probably) over time. Is there anything better to do here than what I have done assuming that this file is going to be fixed?
struct PluginInfo
{
public:
std::string s_Author;
std::string s_Process;
std::string s_ReleaseDate;
//And so on...
struct PluginVersion
{
public:
std::string s_MajorVersion;
std::string s_MinorVersion;
//And so on...
};
PluginVersion o_Version;
//For things we aren't prepared for yet.
void* p_Future;
};
Further, is there any precautions I should take when building shared objects for this system. My hunch is I'll run into lots of library incompatibilities. Please help. Thanks
What about this, or am I thinking too simple?
struct PluginInfo2: public PluginInfo
{
public:
std::string s_License;
};
In your application you are probably passing around only pointers to PluginInfos, so version 2 is compatible to version 1. When you need access to the version 2 members, you can test the version with either dynamic_cast<PluginInfo2 *> or with an explicit pluginAPIVersion member.
Either your plugin is compiled with the same version of C++ compiler and std library source (or its std::string implementation may not be compatible, and all your string fields will break), in which case you have to recompile the plugins anyway, and adding fields to the struct won't matter
Or you want binary compatibility with previous plugins, in which case stick to plain data and fixed size char arrays ( or provide an API to allocate the memory for the strings based on size or passing in a const char* ), in which case it's not unheard of to have a few unused fields in the struct, and then change these to be usefully named items when the need arises. In such cases, it's also common to have a field in the struct to say which version it represents.
But it's very rare to expect binary compatibility and make use of std::string. You'll never be able to upgrade or change your compiler.
As was said by someone else, for binary compatibility you will most likely restrict yourself to a C API.
The Windows API in many places maintains binary compatibility by putting a size member into the struct:
struct PluginInfo
{
std::size_t size; // should be sizeof(PluginInfo)
const char* s_Author;
const char* s_Process;
const char* s_ReleaseDate;
//And so on...
struct PluginVersion
{
const char* s_MajorVersion;
const char* s_MinorVersion;
//And so on...
};
PluginVersion o_Version;
};
When you create such a beast, you need to set the size member accordingly:
PluginInfo pluginInfo;
pluginInfo.size = sizeof(pluginInfo);
// set other members
When you compile your code against a newer version of the API, where the struct has additional members, its size changes, and that is noted in its size member. The API functions, when being passed such a struct presumably will first read its size member and branch into different ways to handle the struct, depending on its size.
Of course, this assumes that evolution is linear and new data is always only added at the end of the struct. That is, you will never have different versions of such a type that have the same size.
However, using such a beast is a nice way of ensuring that user introduce errors into their code. When they re-compile their code against a new API, sizeof(pluginInfo) will automatically adapt, but the additional members won't be set automatically. A reasonably safety would be gained by "initializing" the struct the C way:
PluginInfo pluginInfo;
std::memset( &pluginInfo, 0, sizeof(pluginInfo) );
pluginInfo.size = sizeof(pluginInfo);
However, even putting aside the fact that, technically, zeroing memory might not put a reasonable value into each member (for example, there could be architectures where all bits set to zero is not a valid value for floating point types), this is annoying and error-prone because it requires three-step construction.
A way out would be to design a small and inlined C++ wrapper around that C API. Something like:
class CPPPluginInfo : PluginInfo {
public:
CPPPluginInfo()
: PluginInfo() // initializes all values to 0
{
size = sizeof(PluginInfo);
}
CPPPluginInfo(const char* author /* other data */)
: PluginInfo() // initializes all values to 0
{
size = sizeof(PluginInfo);
s_Author = author;
// set other data
}
};
The class could even take care of storing the strings pointed to by the C struct's members in a buffer, so that users of the class wouldn't even have to worry about that.
Edit: Since it seems this isn't as clear-cut as I thought it is, here's an example.
Suppose that very same struct will in a later version of the API get some additional member:
struct PluginInfo
{
std::size_t size; // should be sizeof(PluginInfo)
const char* s_Author;
const char* s_Process;
const char* s_ReleaseDate;
//And so on...
struct PluginVersion
{
const char* s_MajorVersion;
const char* s_MinorVersion;
//And so on...
};
PluginVersion o_Version;
int fancy_API_version2_member;
};
When a plugin linked to the old version of the API now initializes its struct like this
PluginInfo pluginInfo;
pluginInfo.size = sizeof(pluginInfo);
// set other members
its struct will be the old version, missing the new and shiny data member from version 2 of the API. If it now calls a function of the second API accepting a pointer to PluginInfo, it will pass the address of an old PluginInfo, short one data member, to the new API's function. However, for the version 2 API function, pluginInfo->size will be smaller than sizeof(PluginInfo), so it will be able catch that, and treat the pointer as pointing to an object that doesn't have the fancy_API_version2_member. (Presumably, internal of the host app's API, PluginInfo is the new and shiny one with the fancy_API_version2_member, and PluginInfoVersion1 is the new name of the old type. So all the new API needs to do is to cast the PluginInfo* it got handed be the plugin into a PluginInfoVersion1* and branch off to code that can deal with that dusty old thing.)
The other way around would be a plugin compiled against the new version of the API, where PluginInfo contains the fancy_API_version2_member, plugged into an older version of the host app that knows nothing about it. Again, the host app's API functions can catch that by checking whether pluginInfo->size is greater than the sizeof their own PluginInfo. If so, the plugin presumably was compiled against a newer version of the API than the host app knows about. (Or the plugin write failed to properly initialize the size member. See below for how to simplify dealing with this somewhat brittle scheme.)
There's two ways to deal with that: The simplest is to just refuse to load the plugin. Or, if possible, the host app could work with this anyhow, simply ignoring the binary stuff at the end of the PluginInfo object it was passed which it doesn't know how to interpret.
However, the latter is tricky, since you need to decide this when you implement the old API, without knowing exactly what the new API will look like.
what rwong suggest (std::map<std::string, std::string>) is a good direction. This is makes it possible to add deliberate string fields. If you want to have more flexibility you might declare an abstract base class
class AbstractPluginInfoElement { public: virtual std::string toString() = 0;};
and
class StringPluginInfoElement : public AbstractPluginInfoElement
{
std::string m_value;
public:
StringPluginInfoElement (std::string value) { m_value = value; }
virtual std::string toString() { return m_value;}
};
You might then derive more complex classes like PluginVersion etc. and store a map<std::string, AbstractPluginInfoElement*>.
One hideous idea:
A std::map<std::string, std::string> m_otherKeyValuePairs; would be enough for the next 500 years.
Edit:
On the other hand, this suggestion is so prone to misuse that it may qualify for a TDWTF.
Another equally hideous idea:
a std::string m_everythingInAnXmlBlob;, as seen in real software.
(hideous == not recommended)
Edit 3:
Advantage: The std::map member is not subject to object slicing. When older source code copies an PluginInfo object that contains new keys in the property bag, the entire property bag is copied.
Disadvantage: many programmers will start adding unrelated things to the property bag, and even starts writing code that processes the values in the property bag, leading to maintenance nightmare.
Here's an idea, not sure whether it works with classes, it for sure works with structs: You can make the struct "reserve" some space to be used in the future like this:
struct Foo
{
// Instance variables here.
int bar;
char _reserved[128]; // Make the class 128 bytes bigger.
}
An initializer would zero out whole struct before filling it, so newer versions of the class which would access fields that would now be within the "reserved" area are of sane default values.
If you only add fields in front of _reserved, reducing its size accordingly, and not modify/rearrange other fields you should be OK. No need for any magic. Older software will not touch the new fields as they don't know about them, and the memory footprint will remain the same.
Typically, the way I'd define a true global constant (lets say, pi) would be to place an extern const in a header file, and define the constant in a .cpp file:
constants.h:
extern const pi;
constants.cpp:
#include "constants.h"
#include <cmath>
const pi=std::acos(-1.0);
This works great for true constants such as pi. However, I am looking for a best practice when it comes to defining a "constant" in that it will remain constant from program run to program run, but may change, depending on an input file. An example of this would be the gravitational constant, which is dependent on the units used. g is defined in the input file, and I would like it to be a global value that any object can use. I've always heard it is bad practice to have non-constant globals, so currently I have g stored in a system object, which is then passed on to all of the objects it generates. However this seems a bit clunky and hard to maintain as the number of objects grow.
Thoughts?
It all depends on your application size. If you are truly absolutely sure that a particular constant will have a single value shared by all threads and branches in your code for a single run, and that is unlikely to change in the future, then a global variable matches the intended semantics most closely, so it's best to just use that. It's also something that's trivial to refactor later on if needed, especially if you use distinctive prefixes for globals (such as g_) so that they never clash with locals - which is a good idea in general.
In general, I prefer to stick to YAGNI, and don't try to blindly placate various coding style guides. Instead, I first look if their rationale applies to a particular case (if a coding style guide doesn't have a rationale, it is a bad one), and if it clearly doesn't, then there is no reason to apply that guide to that case.
I can understand the predicament you're in, but I am afraid that you are unfortunately not doing this right.
The units should not affect the program, if you try to handle multiple different units in the heart of your program, you're going to get hurt badly.
Conceptually, you should do something like this:
Parse Input
|
Convert into SI metric
|
Run Program
|
Convert into original metric
|
Produce Output
This ensure that your program is nicely isolated from the various metrics that exist. Thus if one day you somehow add support to the French metric system of the 16th century, you'll just add to configure the Convert steps (Adapters) correctly, and perhaps a bit of the input/output (to recognize them and print them correctly), but the heart of the program, ie the computation unit, would remain unaffected by the new functionality.
Now, if you are to use a constant that is not so constant (for example the acceleration of gravity on earth which depends on the latitude, longitude and altitude), then you can simply pass it as arguments, grouped with the other constants.
class Constants
{
public:
Constants(double g, ....);
double g() const;
/// ...
private:
double mG;
/// ...
};
This could be made a Singleton, but that goes against the (controversed) Dependency Injection idiom. Personally I stray away from Singleton as much as I can, I usually use some Context class that I pass in each method, makes it much easier to test the methods independently from one another.
A legitimate use of singletons!
A singleton class constants() with a method to set the units?
You can use a variant of your latter approach, make a "GlobalState" class that holds all those variables and pass that around to all objects:
struct GlobalState {
float get_x() const;
float get_y() const;
...
};
struct MyClass {
MyClass(GlobalState &s)
{
// get data from s here
... = s.get_x();
}
};
It avoids globals, if you don't like them, and it grows gracefully as more variables are needed.
It's bad to have globals which change value during the lifetime of the run.
A value that is set once upon startup (and remains "constant" thereafter) is a perfectly acceptable use for a global.
Why is your current solution going to be hard to maintain? You can split the object up into multiple classes as it grows (one object for simulation parameters such as your gravitational constant, one object for general configuration, and so on)
My typical idiom for programs with configurable items is to create a singleton class named "configuration". Inside configuration go things that might be read from parsed configuration files, the registry, environment variables, etc.
Generally I'm against making get() methods, but this is my major exception. You can't typically make your configuration items consts if they have to be read from somewhere at startup, but you can make them private and use const get() methods to make the client view of them const.
This actually brings to mind the C++ Template Metaprogramming book by Abrahams & Gurtovoy - Is there a better way to manage your data so that you don't get poor conversions from yards to meters or from volume to length, and maybe that class knows about gravity being a form acceleration.
Also you already have a nice example here, pi = the result of some function...
const pi=std::acos(-1.0);
So why not make gravity the result of some function, which just happens to read that from file?
const gravity=configGravity();
configGravity() {
// open some file
// read the data
// return result
}
The problem is that because the global is managed prior to main being called you cannot provide input into the function - what config file, what if the file is missing or doesn't have g in it.
So if you want error handling you need to go for a later initialization, singletons fit that better.
Let's spell out some specs. So, you want:
(1) the file holding the global info (gravity, etc.) to outlive your runs of the executable using them;
(2) the global info to be visible in all your units (source files);
(3) your program to not be allowed to change the global info, once read from the file;
Well,
(1) Suggests a wrapper around the global info whose constructor takes an ifstream or file name string reference (hence, the file must exist before the constructor is called and it will still be there after the destructor is invoked);
(2) Suggests a global variable of the wrapper. You may, additionally, make sure that that is the only instance of this wrapper, in which case you need to make it a singleton as was suggested. Then again, you may not need this (you may be okay with having multiple copies of the same info, as long as it is read-only info!).
(3) Suggests a const getter from the wrapper. So, a sample may look like this:
#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>//for EXIT_FAILURE
using namespace std;
class GlobalsFromFiles
{
public:
GlobalsFromFiles(const string& file_name)
{
//...process file:
std::ifstream ginfo_file(file_name.c_str());
if( !ginfo_file )
{
//throw SomeException(some_message);//not recommended to throw from constructors
//(definitely *NOT* from destructors)
//but you can... the problem would be: where do you place the catcher?
//so better just display an error message and exit
cerr<<"Uh-oh...file "<<file_name<<" not found"<<endl;
exit(EXIT_FAILURE);
}
//...read data...
ginfo_file>>gravity_;
//...
}
double g_(void) const
{
return gravity_;
}
private:
double gravity_;
};
GlobalsFromFiles Gs("globals.dat");
int main(void)
{
cout<<Gs.g_()<<endl;
return 0;
}
Globals aren't evil
Had to get that off my chest first :)
I'd stick the constants into a struct, and make a global instance of that:
struct Constants
{
double g;
// ...
};
extern Constants C = { ... };
double Grav(double m1, double m2, double r) { return C.g * m1 * m2 / (r*r); }
(Short names are ok, too, all scientists and engineers do that.....)
I've used the fact that local variables (i.e. members, parameters, function-locals, ..) take precedence over the global in a few cases as "apects for the poor":
You could easily change the method to
double Grav(double m1, double m2, double r, Constants const & C = ::C)
{ return C.g * m1 * m2 / (r*r); } // same code!
You could create an
struct AlternateUniverse
{
Constants C;
AlternateUniverse()
{
PostulateWildly(C); // initialize C to better values
double Grav(double m1, double m2, double r) { /* same code! */ }
}
}
The idea is to write code with least overhead in the default case, and preserving the implementation even if the universal constants should change.
Call Scope vs. Source Scope
Alternatively, if you/your devs are more into procedural rather thsn OO style, you could use call scope instead of source scope, with a global stack of values, roughly:
std::deque<Constants> g_constants;
void InAnAlternateUniverse()
{
PostulateWildly(C); //
g_constants.push_front(C);
CalculateCoreTemp();
g_constants.pop_front();
}
void CalculateCoreTemp()
{
Constants const & C= g_constants.front();
// ...
}
Everything in the call tree gets to use the "most current" constants. OYu can call the same tree of coutines - no matter how deeply nested - with an alternate set of constants. Of course it should be encapsulated better, made exception safe, and for multithreading you need thread local storage (so each thread gets it's own "stack")
Calculation vs. User Interface
We approach your original problem differently: All internal representation, all persistent data uses SI base units. Conversion takes place at input and output (e.g. even though the typical size is millimeter, it's always stored as meter).
I can't really compare, but worksd very well for us.
Dimensional Analysis
Other replies have at least hinted at Dimensional Analysis, such as the respective Boost Library. It can enforce dimensional correctness, and can automate the input / output conversions.
Setting:
A pseudo-random pattern has to be generated. There are several ways / or algorithms availible to create different content. All algorithms will generate a list of chars (but could be anything else)... the important part is, that all of them return the same type of values, and need the same type of input arguments.
It has to be possible to call a method GetRandomPattern(), which will use a random one of the algorithms everytime it is called.
My first aproach was to put each algorithm in it's own function and select a random one of them each time GetRandompattern() is called. But I didn't come up with another way of choosing between them, than with a switch case statement which is unhandy, ugly and inflexible.
class PatternGenerator{
public:
list<char> GetRandomPattern();
private:
list<char>GeneratePatternA(foo bar);
list<char>GeneratePatternB(foo bar);
........
list<char>GeneratePatternX(foo bar);
}
What would be a good way to select a random GeneratePattern function every time the GetRandomPattern() method is called ?
Or should the whole class be designed differently ?
Thanks a lot
Create a single class for each algorithm, each one subclassing a generator class. Put instances of those objects into a list. Pick one randomly and use it!
More generically, if you start creating several alternative methods with the same signature, something's screaming "put us into sibling classes" at you :)
Update
Can't resist arguing a bit more for an object-oriented solution after the pointer-suggestion came
Imagine at some point you want to print which method created which random thing. With objects, it's easy, just add a "name" method or something. How do you want to achieve this if all you got is a pointer? (yea, create a dictionary from pointers to strings, hm...)
Imagine you find out that you got ten methods, five of which only differ by a parameter. So you write five functions "just to keep the code clean from OOP garbage"? Or won't you rather have a function which happens to be able to store some state with it (also known as an object?)
What I'm trying to say is that this is a textbook application for some OOP design. The above points are just trying to flesh that out a bit and argue that even if it works with pointers now, it's not the future-proof solution. And you shouldn't be afraid to produce code that talks to the reader (ie your future you, in four weeks or so) telling that person what it's doing
You can make an array of function pointers. This avoids having to create a whole bunch of different classes, although you still have to assign the function pointers to the elements of the array. Any way you do this, there are going to be a lot of repetitive-looking lines. In your example, it's in the GetRandomPattern method. In mine, it's in the PatternGenerator constructor.
#define FUNCTION_COUNT 24
typedef list<char>(*generatorFunc)(foo);
class PatternGenerator{
public:
PatternGenerator() {
functions[0] = &GeneratePatternA;
functions[1] = &GeneratePatternB;
...
functions[24] = &GeneratePatternX;
}
list<char> GetRandomPattern() {
foo bar = value;
int funcToUse = rand()%FUNCTION_COUNT;
functions[funcToUse](bar);
}
private:
generatorFunc functions[FUNCTION_COUNT];
}
One way to avoid switch-like coding is using Strategy design pattern. As example:
class IRandomPatternGenerator
{
public:
virtual list<int> makePattern(foo bar);
};
class ARandomPatternGenerator : public IRandomPatternGenerator
{
public:
virtual list<int> makePattern(foo bar)
{
...
}
};
class BRandomPatternGenerator : public IRandomPatternGenerator
{
public:
virtual list<int> makePattern(foo bar)
{
...
}
};
Then you can choose particular algorithm depending on runtime type of your RandomPatternGenerator instance. (As example creating list like nicolas78 suggested)
Thank you for all your great input.
I decided to go with function pointers, mainly because I didn't know them before and they seem to be very powerfull and it was a good chance to get to know them, but also because it saves me lot of lines of code.
If I'd be using Ruby / Java / C# I'd have decided for the suggested Strategy Design pattern ;-)
class PatternGenerator{
typedef list<char>(PatternGenerator::*createPatternFunctionPtr);
public:
PatternGenerator(){
Initialize();
}
GetRandomPattern(){
int randomMethod = (rand()%functionPointerVector.size());
createPatternFunctionPtr randomFunction = functionPointerVector.at( randomMethod );
list<char> pattern = (this->*randomFunction)();
return pattern;
}
private:
void Initialize(){
createPatternFunctionPtr methodA = &PatternGenerator::GeneratePatternA;
createPatternFunctionPtr methodB = &PatternGenerator::GeneratePatternB;
...
functionPointerVector.push_back( methodA );
functionPointerVector.push_back( methodB );
}
list<char>GeneratePatternA(){
...}
list<char>GeneratePatternB(){
...}
vector< createPattern > functionPointerVector;
The readability is not much worse as it would have been with the Design Pattern Solution, it's easy to add new algorithms, the pointer arithmetics are capsuled within a class, it prevents memory leaks and it's very fast and effective...