Sorry for the atrocious title.
The scenario is that I have a configuration system in my program, that needs to able to hold values of all types. These values (configurants) need to be held in a homogenous container.
For example: (this is pseudo-code, I'm not sure how this would actually look)
configurant i(64);
configurant b(true);
configurant f(128.f);
configurant custom(Color(255, 255, 255));
vector<configurant> confs;
confs.emplace_back(i);
confs.emplace_back(b);
confs.emplace_back(f);
confs.emplace_back(custom);
// some form of accessing the vector with proper configurant type here:
// casting?
Like I said, I don't know how this system would look in practice. I know that a statement like
auto color = confs.at(3).rgb();
is generally not possible in C++, but is there something I could do with templated metaprogramming to try and get as close as possible to this solution?
(perhaps the configurants could be mapped with their type? but this would not be a compile time operation)
I'm looking to create a system that is storable homogeneously, immediately accessible (not stored on the heap) and that evaluates the validity of operations at compile time.
Open to any suggestions.
And before the comments come in, I've experimented with std::any/std::any_cast, std::variant, the visitor pattern, and other things of that nature. I'm not looking to use any of those systems.
EDIT To avoid confusion, there are N configurants: it is not a statically sized group of configurants. More configurants would be added by the user of this interface.
EDIT 2 Additional example of desired usage
class foo {
configurant enabled;
configurant bar;
public:
foo() {
this->enabled = configurant(true);
this->bar = configurant(5);
}
void body() {
if(this->enabled) {
std::cout << (this->bar < 100) << "\n";
}
}
// It also needs to be kept in mind that these configurant classes
// cannot be templated (directly, at least), since they need to be stored in
// a homogeneous container.
};
Because of this requirement
"that evaluates the validity of operations at compile time.",
this means that your example
auto color = confs.at(3).rgb();
will only work with the index 3 known at compile time
(why not 2 or 4?).
This index is not very relevant/useful in this situation.
May be should you simply consider a structure providing
the required data with a proper name instead of a
compile-time index?
struct Confs
{
configurant<int> i;
configurant<bool> b;
configurant<float> f;
configurant<Color> custom;
};
...
Confs confs{64, true, 128.f, Color(255, 255, 255)};
auto color = confs.custom.rgb();
Something like this could rely on a compile-time index
but I don't really see the benefit over a named member.
auto confs=std::make_tuple(64, true, 128.0f, Color{255, 255, 255});
auto color = std::get<3>(confs).rgb();
Related
I have 3 structs : Student, Citizen, Employee. I want user to be able to choose what struct they want to work with (std::vector of structs, actually). Since there's no way to define type at runtime, I created all 3 vectors, but will use only one of them (depending on the user's choice), others will stay empty:
std::vector<Student> container_student;
std::vector<Citizen> container_citizen;
std::vector<Employee> container_employee;
auto containers = make_tuple(container_student, container_citizen, container_employee);
std::cout << "Enter:\n0 to operate \"Student\" struct\n1 to operate \"Citizen\" struct\n2 to operate \"Employee\" struct\n";
std::cin >> container_type;
auto container = std::get<container_type>(containers);
But I get No matching function for call to 'get', even though container_type is an int and containers is a tuple.
Edit: understandable, auto can't make magic and I still try to make container's type to depend on runtime. But even if I try to use std::get<container_type>(containers) (probably define would help) instead of container in functions etc., I get the same error, which is not understandable.
Unfortunately, what you're proposing isn't possible in C++. The C++ typing and template system works at compile-time, where information read in from the user isn't available. As a result, anything passed into a template's angle braces needs to be determinable at compile-time. In your case, the number the user enters, indicating which option they want to select, is only knowable at runtime.
There are some routes you could take to achieve the same result, though. For example, one option would be to do something like this:
if (container_type == 0) {
auto container = std::get<0>(containers);
/* ... */
} else if (container_type == 1) {
auto container = std::get<1>(containers);
/* ... */
} /* etc */
Here, all the template angle braces are worked out at compile-time. (Then again, if this is what you're going to be doing, you wouldn't need the tuple at all. ^_^)
Another option would be to use templates, like this:
template <typename T> void doSomething(std::vector<T>& container) {
/* Put your code here */
}
/* Then, back in main... */
if (container_type == 0) {
doSomething(container_student);
} else if (container_type == 1) {
doSomething(container_citizen);
} /* etc */
This still requires you to insert some code to map from integer types to the functions you want to call, but it leaves you the freedom to have a container variable (the one in doSomething) that you can treat generically at that point.
It's basically the Fundamental Theorem of Software Engineering in action - all problems can be solved by adding another layer of indirection. :-)
Hope this helps!
I'm using the Qt framework to create a ui for my business logic.
The class responsible for building the ui provides several methods which, step by step, initialize the ui elements, layout them, group them and, finally, format (i.e. void MyUi::init3_formatUiElements()) them.
Naturally, some ui elements need numerous layout settings set, so this method might look like
void MyUi::init3_formatUiElements() {
_spinBox_distance->setMinimum(0.0);
_spinBox_distance->setMaximum(10.0);
_spinBox_distance->setSingleStep(0.5);
_spinBox_distance->setSuffix(" meters");
//...
//same for other widgets
return;
}
Objects like QDoubleSpinBox* _spinBox_distance are member fields of the MyUi class.
I would like to have a "temporary alias" for _spinBox_distance, in that the above method body simplifies to
void MyUi::init3_formatUiElements() {
//create alias x for _spinBox_distance here
x->setMinimum(0.0);
x->setMaximum(10.0);
x->setSingleStep(0.5);
x->setSuffix(" meters");
//...
//free alias x here
//same for other widgets: create alias x for next widget
//...
//free alias x here
return;
}
This would speed up the typing process and would make code fragments more copy/paste-able, especially for ui elements of a similar type.
Apart from scoping each block in curly braces
{ QDoubleSpinBox*& x = _spinBox_distance;
x->setMinimum(0.0);
//...
}
{ QLabel*& x = _label_someOtherWidget;
//...
}
is there an elegant way to achieve this?
I tried the above syntax without scoping, but destructing x then of course leads to destruction of the underlying widget.
Maybe
QDoubleSpinBox** x = new QDoubleSpinBox*;
x = &_spinBox_distance;
(*x)->setMinimum(0.0);
//...
delete x;
but that doesn't make things much more type-easy (three extra lines, pointers to pointers, (*x))... :D
EDIT: This one does not work as after delete x, can't be redeclared another type.
What about using a macro ?
#define Set(argument) _spinBox_distance->set##argument
and
Set(Minimum(0.0));
Set(Maximum(10.0));
Set(SingleStep(0.5));
Set(Suffix(" meters"));
Or
#define Set(Argument, Value) _spinBox_distance->set##argument(Value)
Set(Minimum, 0.0);
Set(Maximum, 10.0);
Set(SingleStep, 0.5);
Set(Suffix, " meters");
Collecting the fundamental conceptual thoughts about the problem in question from the comments section, I may post the syntactical/technical answer to the question. This approach, without a doubt, should not be chosen in any kind of "complex" situation (or rather not at all).
bad coding style:
same name for different things
name which doesn't tell you anything about the object
move repeated code to dedicated functions, which...
may specialize on several ui types
are template functions
...
in case of Qt: Use Qt Designer.
...
{ auto x = _spinBox_distance;
x->setMinimum(0.0);
//...
}
{ auto x = _label_someOtherWidget;
//...
}
will do the trick.
I think your code looks fine as it is, I find it much more useful to have the code be easy to read/understand than it is to have the code be easy to write. Remember that you write the code once, then have to read it many times afterwards.
In cases like this I make it easier to write with good old (and oft blamed for mistakes) copy and paste. Grab _spinBox_distance->set and just paste, finish the line, paste, finish the line, etc...
If, however, you find yourself writing those 4 setters in a row over and over again, then put them in 1 function that takes in the 4 parameters.
void SetParameters(QDoubleSpinBox* spinBox_distance, double min, double max, double step, std::string suffix)
{
//the setters
}
Context: I'm trying to memoize an object of a template class. Right now, the class is a deeply nested data structure full of unique pointers, and so doesn't have a copy constructor (and so would be impossible to cache, as far as I know). However, in the future, I would like to allow memoization if a copy constructor is available. I tried the following code:
// some function here... {
static std::unordered_map<State, Result> cache;
return [c, ToValue](State state) {
if (cache.find(state) != cache.end()) {
std::cout << "retrieving Literal from cache\n";
if (std::is_copy_constructible<Result>::value) {
return cache[state];
}
}
// calculate and return a Result
This code doesn't compile because Result doesn't have a copy constructor. Is there any way to get around this? Google is being quite unhelpful.
I'm presuming the error you are getting is that return cache[state]; cannot be compiled when the object is not copy-constructible. To fix that you can write:
if constexpr (std::is_copy_constructible<Result>::value) {
return cache[state];
}
If you are still having trouble then post a MCVE that has the error.
As others have commented, the question is rather ill-defined and a bit confused, but do you need to actually copy an object in order to cache it?
Actually, no. You can use std::shared_ptr to share ownership of the object between the creator, any consumers, and the cache. If nothing else, this is much more efficient if your object is a complex one. It will also work for any type of object, copyable or not.
Example (I'm going to use the word Key rather than State, for what I hope are obvious reasons).
Given these declarations:
class MyKey
{
// ....
};
class MyCacheableObject
{
// Constructor
MyCacheableObject (int a, int b, int c) { ... }
// ...
};
static std::unordered_map<MyKey, std::shared_ptr<MyCacheableObject>> cache; // or std::map
You can do this (please note that there are other ways to make a std::shared_ptr, see here):
std::shared_ptr<MyCacheableObject> CreateCacheableObject (int a, int b, int c)
{
return std::make_shared<MyCacheableObject> (MyCacheableObject (a, b, c));
}
And then, assuming you have a key you plan to use to retrieve the object from the cache later on, you can do:
MyKey someKey = ...;
std::shared_ptr<MyCacheableObject> newObject = CreateCacheableObject (1, 2, 3);
// ... setup / use `newObject` in whatever way is appropriate to your use-case
cache [someKey] = newObject;
And you can of course retrieve the object from the cache (if it's in there) via:
auto retrievedObject = cache.find (someKey)
if (retrievedObject != cache.end())
...
So this question is not about whether an object is copyable at all. It's about (shared) ownership and std::shared_ptr takes care of all that for you, you don't really have to think about it. Oy vay.
There's a live demo, to show that this all compiles, here.
We have a client/server application where older servers are supported by newer clients. Both support a shared set of icons.
We're representing the icons as an implicit enum that's included in the server and client builds:
enum icons_t { // rev 1.0
ICON_A, // 0
ICON_B, // 1
ICON_C // 2
};
Sometimes we retire icons (weren't being used, or used internally and weren't listed in our API), which led to the following code being committed:
enum icons_t { // rev 2.0
ICON_B, // 0
ICON_C // 1 (now if a rev 1.0 server uses ICON_B, it will get ICON_C instead)
};
I've changed our enum to the following to try and work around this:
// Big scary header about commenting out old icons
enum icons_t { // rev 2.1
// Removed: ICON_A = 0,
ICON_B = 1,
ICON_C = 2
};
Now my worry is a bad merge when multiple people add new icons:
// Big scary header about commenting out old icons
enum icons_t { // rev 30
// Removed: ICON_A = 0,
ICON_B = 1,
ICON_C = 2,
ICON_D = 3,
ICON_E = 3 // Bad merge leaves 2 icons with same value
};
Since it's an enum we don't really have a way to assert if the values aren't unique.
Is there a better data structure to manage this data, or a design change that wouldn't be open to mistakes like this? My thoughts have been going towards a tool to analyze pull requests and block merges if this issue is detected.
I have previously done tests that check out previous builds and scan header files for this type of version-breaking behaviour. You can use diff to generate a report of any changes, grep that for the common pattern, and identify the difference between deleting a fixed-index entry, changing the index of an entry, and deleting or inserting a floating index entry.
The one obvious way to avoid it is to NOT remove the dead indices, but rename them, i.e. ICON_A becomes ICON_A_RETIRED, and its slot is reserved for ever. It is inevitable that someone will change an index, though, so a good unit test would also help. Forcing a boilerplate style means the test is simpler than coping with the generic case.
Another trick might be to accept that the issue will occur, but if it is only a problem for customers, and at each software release/revision, update a base number for the range, release the software and update again, so the dev version is never compatible with the release, eg
#define ICON_RANGE 0x1000
#define ICON_REVISION_BASE ((RELEASENUM+ISDEVFLAG)*ICON_RANGE)
enum icon_t {
iconTMax = ICON_REVISION_BASE+ICON_RANGE,
iconTBase = ICON_REVISION_BASE,
icon_A,
icon_B,
Then, at run-time, any icons not in the current range are easily rejected, or you might provide a special look-up between versions, perhaps generated by trawling your version control revisions. Note that you can only provide backward compatibility this way, not forward compatibility. It would be up to newer code to preemptively back-translate their icon numbers to send to older modules, which may be more effort than it is worth.
This thought just crossed my mind: if we keep a literal at the end for the enum size, our unit tests can use that to assert if we haven't verified each enum literal:
enum icons_t {
ICON_A_DEPRECATED,
ICON_B,
ICON_C,
ICON_COUNT // ALWAYS KEEP THIS LAST
};
Then in testing:
unsigned int verifyCount = 0;
verify(0, ICON_A_DEPRECATED); // verifyCount++, assert 0 was not verified before
verify(1, ICON_B); // verifyCount++, assert 1 was never verified before
assert(ICON_COUNT == verifyCount, "Not all icons verified");
Then our only problem is ensuring tests pass before releasing, which we should be doing anyway.
Since the question has been tagged C++11, this could be better handled with Scoped enumerations.
Read about it here : http://en.cppreference.com/w/cpp/language/enum
Since the same enum file is included in both client and server, then removing any entry would lead to compilation failure in places a missing entry is being used.
All that need to be changed, is your icon_t.
Upgrade it from enum to enum class
enum class icon_t
{
ICON_A,
ICON_B,
};
Now you can't blatantly pass int instead of an icon_t. This reduces your probability to make mistakes drastically.
So the calling side
#include <iostream>
enum class icon_t
{
ICON_A,
ICON_B,
};
void test_icon(icon_t const & icon)
{
if (icon == icon_t::ICON_A)
std::cout << "icon_t::ICON_A";
if (icon == icon_t::ICON_B)
std::cout << "icon_t::ICON_B";
}
int main()
{
auto icon = icon_t::ICON_A;
test_icon(icon); // this is ok
test_icon(1); // Fails at compile time : no known conversion from 'int' to 'const icon_t' for 1st argument
return 0;
}
Moreover, extracting numerical values is allowed from Scoped Enumerators. static_cast to int is allowed. If required.
int n = static_cast<int>(icon); // Would return 0, the index of icon_t::ICON_A
I'm building a simple generic engine for my true start in the making of games, and I am trying to be somehow organized and decent in the making of my engine, meaning I don't want it to be something I throw to the side once I make what I'm planning to.
I add objects to be displayed, drawObjects, and these can either move, not move, and have an animation, or not have one.
In case they DO have an animation, I want to initialize a single animationSet, and this animationSet will have xxx animationComp inside of it. As I'm trying to be neat and have worked abit on "optimizations" towards memory and cpu usage (such as sharing already-loaded image pointers, and whatever came across my mind), I wanted to not ask for possibly unused memory in arrays.
So I had animationSetS* animationSet = NULL; initially, planning to do a animationSet = animationSetS[spacesINEED]; after, only on the objects that needed animation that I added, being those that aren't animations a NULL and therefore not using memory (correct?).
And then this question popped up! (title)
struct animationComp {
SDL_Rect* clip;
int clipsize;
};
struct animationSetS {
animationComp* animation;
int currentFrame;
int currentAnimation;
int animationNumber;
};
struct drawObject { // Um objecto.
char* name;
SDL_Surface* surface;
bool draw = true;
float xPos;
float yPos;
bool willMove = false; // 0 - Won't move, 10 - Moves alot, TO IMPLEMENT
bool isSprite = false;
animationSetS* animationSet;
};
I dabble alot in my questions, sorry for that. For any clarifications reply here, I'll reply within 10 minutes for the next... 1 hour perhaps? Or more.
Thanks!
Setting the pointer to NULL means that you'll be able to add ASSERT(ptr != NULL); and KNOW that your pointer does not accidentally contain some rubbish value from whatever happens to be in the memory it was using.
So, if for some reason, you end up using the object before it's been properly set up, you can detect it.
It also helps if you sometimes don't use a field, you can still call delete stuff; [assuming it's allocated in the first place].
Note that leaving a variable uninitialized means that it can have ANY value within it's valid range [and for some types, outside the valid range - e.g. pointers and floating point values can be "values that are not allowed by the processor"]. This means that it's impossible to "tell" within the code if it has been initialized or not - but things will go horribly wrong if you don't initialize things!
If this should be really implemented in C++ (as you write), why don't you use the C++ Standard Library? Like
struct animationSetS {
std::vector< std::shared_ptr<animationComp> > animation;
// ...
}