Edit:
So this question was misinterpreted to such a ludicrous degree that it has no point anymore. I don't know how, since the question that I actually asked was whether my specific implementation of this—yes, known to be pointless, yes, not remotely resembling idiomatic C++—macro was as good as it could be, and whether it necessarily had to use auto, or if there was a suitable workaround instead. It was not supposed to generate this much attention, and certainly not a misunderstanding of this magnitude. It's pointless to ask respondents to edit their answers, I don't want anybody to lose reputation over this, and there's some good information floating around in here for potential future viewers, so I'm going to arbitrarily pick one of the lower-voted answers to evenly distribute the reputation involved. Move along, nothing to see here.
I saw this question and decided it might be fun to write a with statement in C++. The auto keyword makes this really easy, but is there a better way to do it, perhaps without using auto? I've elided certain bits of the code for brevity.
template<class T>
struct with_helper {
with_helper(T& v) : value(v), alive(true) {}
T* operator->() { return &value; }
T& operator*() { return value; }
T& value;
bool alive;
};
template<class T> struct with_helper<const T> { ... };
template<class T> with_helper<T> make_with_helper(T& value) { ... }
template<class T> with_helper<const T> make_with_helper(const T& value) { ... }
#define with(value) \
for (auto o = make_with_helper(value); o.alive; o.alive = false)
Here's an (updated) usage example with a more typical case that shows the use of with as it is found in other languages.
int main(int argc, char** argv) {
Object object;
with (object) {
o->member = 0;
o->method(1);
o->method(2);
o->method(3);
}
with (object.get_property("foo").perform_task(1, 2, 3).result()) {
std::cout
<< (*o)[0] << '\n'
<< (*o)[1] << '\n'
<< (*o)[2] << '\n';
}
return 0;
}
I chose o because it's an uncommon identifier, and its form gives the impression of a "generic thing". If you've got an idea for a better identifier or a more usable syntax altogether, then please do suggest it.
If you use auto, why use macros at all?
int main()
{
std::vector<int> vector_with_uncommonly_long_identifier;
{
auto& o = vector_with_uncommonly_long_identifier;
o.push_back(1);
o.push_back(2);
o.push_back(3);
}
const std::vector<int> constant_duplicate_of_vector_with_uncommonly_long_identifier
(vector_with_uncommonly_long_identifier);
{
const auto& o = constant_duplicate_of_vector_with_uncommonly_long_identifier;
std::cout
<< o[0] << '\n'
<< o[1] << '\n'
<< o[2] << '\n';
}
{
auto o = constant_duplicate_of_vector_with_uncommonly_long_identifier.size();
std::cout << o <<'\n';
}
}
EDIT: Without auto, just use typedef and references.
int main()
{
typedef std::vector<int> Vec;
Vec vector_with_uncommonly_long_identifier;
{
Vec& o = vector_with_uncommonly_long_identifier;
o.push_back(1);
o.push_back(2);
o.push_back(3);
}
}
?? attempted vb syntax into C++
with says do all the things in the following block by default referencing the object I've said to do it with right? Executes a series of statements making repeated reference to a single object or structure.
with(a)
.do
.domore
.doitall
so how is the example giving you the same syntax?
to me examples of why to use a with where multiple de referencess
so rather than
book.sheet.table.col(a).row(2).setColour
book.sheet.table.col(a).row(2).setFont
book.sheet.table.col(a).row(2).setText
book.sheet.table.col(a).row(2).setBorder
you have
with( book.sheet.table.col(a).row(2) )
.setColour
.setFont
.setText
.setBorder
seems just as easy, and more common syntax in C++ to
cell& c = book.sheet.table.col(a).row(2);
c.setColour
c.setFont
c.setText
c.setBorder
For C++0x (which you're assuming):
int main() {
std::vector<int> vector_with_uncommonly_long_identifier;
{
auto& o = vector_with_uncommonly_long_identifier;
o.push_back(1);
o.push_back(2);
o.push_back(3);
}
}
Why not just use a good lambda?
auto func = [&](std::vector<int>& o) {
};
func(vector_with_a_truly_ridiculously_long_identifier);
The simple fact is that if your identifiers are so long, that you can't type them out every time, use a reference, function, pointer, etc to solve this problem, or better, refactor the name. Statements like this (e.g. using() in C#) have additional side effects (deterministic cleanup, in my example). Your statement in C++ has no notable actual benefits, since it doesn't actually invoke any additional behaviour against just writing the code out.
Related
I have a Storage class that keeps a list of Things:
#include <iostream>
#include <list>
#include <functional>
class Thing {
private:
int id;
int value = 0;
static int nextId;
public:
Thing() { this->id = Thing::nextId++; };
int getId() const { return this->id; };
int getValue() const { return this->value; };
void add(int n) { this->value += n; };
};
int Thing::nextId = 1;
class Storage {
private:
std::list<std::reference_wrapper<Thing>> list;
public:
void add(Thing& thing) {
this->list.push_back(thing);
}
Thing& findById(int id) const {
for (std::list<std::reference_wrapper<Thing>>::const_iterator it = this->list.begin(); it != this->list.end(); ++it) {
if (it->get().getId() == id) return *it;
}
std::cout << "Not found!!\n";
exit(1);
}
};
I started with a simple std::list<Thing>, but then everything is copied around on insertion and retrieval, and I didn't want this because if I get a copy, altering it does not reflect on the original objects anymore. When looking for a solution to that, I found about std::reference_wrapper on this SO question, but now I have another problem.
Now to the code that uses them:
void temp(Storage& storage) {
storage.findById(2).add(1);
Thing t4; t4.add(50);
storage.add(t4);
std::cout << storage.findById(4).getValue() << "\n";
}
void run() {
Thing t1; t1.add(10);
Thing t2; t2.add(100);
Thing t3; t3.add(1000);
Storage storage;
storage.add(t3);
storage.add(t1);
storage.add(t2);
temp(storage);
t2.add(10000);
std::cout << storage.findById(2).getValue() << "\n";
std::cout << storage.findById(4).getValue() << "\n";
}
My main() simply calls run(). The output I get is:
50
10101
Not found!!
Although I was looking for:
50
10101
50
Question
Looks like the locally declared object t4 ceases to exist when the function returns, which makes sense. I could prevent this by dynamically allocating it, using new, but then I didn't want to manage memory manually...
How can I fix the code without removing the temp() function and without having to manage memory manually?
If I just use a std::list<Thing> as some suggested, surely the problem with t4 and temp will cease to exist, but another problem will arise: the code won't print 10101 anymore, for example. If I keep copying stuff around, I won't be able to alter the state of a stored object.
Who is the owner of the Thing in the Storage?
Your actual problem is ownership. Currently, your Storage does not really contain the Things but instead it is left to the user of the Storage to manage the lifetime of the objects you put inside it. This is very much against the philosophy of std containers. All standard C++ containers own the objects you put in them and the container manages their lifetime (eg you simply call v.resize(v.size()-2) on a vector and the last two elements get destroyed).
Why references?
You already found a way to make the container not own the actual objects (by using a reference_wrapper), but there is no reason to do so. Of a class called Storage I would expect it to hold objects not just references. Moreover, this opens the door to lots of nasty problems, including undefined behaviour. For example here:
void temp(Storage& storage) {
storage.findById(2).add(1);
Thing t4; t4.add(50);
storage.add(t4);
std::cout << storage.findById(4).getValue() << "\n";
}
you store a reference to t4 in the storage. The thing is: t4s lifetime is only till the end of that function and you end up with a dangling reference. You can store such a reference, but it isnt that usefull because you are basically not allowed to do anything with it.
Aren't references a cool thing?
Currently you can push t1, modify it, and then observe that changes on the thingy in Storage, this might be fine if you want to mimic Java, but in c++ we are used to containers making a copy when you push something (there are also methods to create the elements in place, in case you worry about some useless temporaries). And yes, of course, if you really want you can make a standard container also hold references, but lets make a small detour...
Who collects all that garbage?
Maybe it helps to consider that Java is garbage-collected while C++ has destructors. In Java you are used to references floating around till the garbage collector kicks in. In C++ you have to be super aware of the lifetime of your objects. This may sound bad, but acutally it turns out to be extremely usefull to have full control over the lifetime of objects.
Garbage? What garbage?
In modern C++ you shouldnt worry to forget a delete, but rather appreciate the advantages of having RAII. Acquiring resources on initialzation and knowing when a destructor gets called allows to get automatic resource management for basically any kind of resource, something a garbage collector can only dream of (think of files, database connections, etc.).
"How can I fix the code without removing the temp() function and without having to manage memory manually?"
A trick that helped me a lot is this: Whenever I find myself thinking I need to manage a resource manually I stop and ask "Can't someone else do the dirty stuff?". It is really extremely rare that I cannot find a standard container that does exactly what I need out of the box. In your case, just let the std::list do the "dirty" work.
Can't be C++ if there is no template, right?
I would actually suggest you to make Storage a template, along the line of:
template <typename T>
class Storage {
private:
std::list<T> list;
//....
Then
Storage<Thing> thing_storage;
Storage<int> int_storage;
are Storages containing Things and ints, respectively. In that way, if you ever feel like exprimenting with references or pointers you could still instantiate a Storage<reference_wrapper<int>>.
Did I miss something?...maybe references?
I won't be able to alter the state of a stored object
Given that the container owns the object you would rather let the user take a reference to the object in the container. For example with a vector that would be
auto t = std::vector<int>(10,0); // 10 element initialized to 0
auto& first_element = t[0]; // reference to first element
first_element = 5; // first_element is an alias for t[0]
std::cout << t[0]; // i dont want to spoil the fun part
To make this work with your Storage you just have to make findById return a reference. As a demo:
struct foo {
private:
int data;
public:
int& get_ref() { return data;}
const int& get_ref() const { return data;}
};
auto x = foo();
x.get_ref = 12;
TL;DR
How to avoid manual resource managment? Let someone else do it for you and call it automatic resource management :P
t4 is a temporary object that is destroyed at exit from temp() and what you store in storage becomes a dangling reference, causing UB.
It is not quite clear what you're trying to achieve, but if you want to keep the Storage class the same as it is, you should make sure that all the references stored into it are at least as long-lived as the storage itself. This you have discovered is one of the reasons STL containers keep their private copies of elements (others, probably less important, being—elimination of an extra indirection and a much better locality in some cases).
P.S. And please, can you stop writing those this-> and learn about initialization lists in constructors? >_<
In terms of what your code actually appears to be doing, you've definitely overcomplicated your code, by my estimation. Consider this code, which does all the same things your code does, but with far less boilerplate code and in a way that's far more safe for your uses:
#include<map>
#include<iostream>
int main() {
std::map<int, int> things;
int & t1 = things[1];
int & t2 = things[2];
int & t3 = things[3];
t1 = 10;
t2 = 100;
t3 = 1000;
t2++;
things[4] = 50;
std::cout << things.at(4) << std::endl;
t2 += 10000;
std::cout << things.at(2) << std::endl;
std::cout << things.at(4) << std::endl;
things.at(2) -= 75;
std::cout << things.at(2) << std::endl;
std::cout << t2 << std::endl;
}
//Output:
50
10101
50
10026
10026
Note that a few interesting things are happening here:
Because t2 is a reference, and insertion into the map doesn't invalidate references, t2 can be modified, and those modifications will be reflected in the map itself, and vise-versa.
things owns all the values that were inserted into it, and it will be cleaned up due to RAII, and the built-in behavior of std::map, and the broader C++ design principles it is obeying. There's no worry about objects not being cleaned up.
If you need to preserve the behavior where the id incrementing is handled automatically, independently from the end-programmer, we could consider this code instead:
#include<map>
#include<iostream>
int & insert(std::map<int, int> & things, int value) {
static int id = 1;
int & ret = things[id++] = value;
return ret;
}
int main() {
std::map<int, int> things;
int & t1 = insert(things, 10);
int & t2 = insert(things, 100);
int & t3 = insert(things, 1000);
t2++;
insert(things, 50);
std::cout << things.at(4) << std::endl;
t2 += 10000;
std::cout << things.at(2) << std::endl;
std::cout << things.at(4) << std::endl;
things.at(2) -= 75;
std::cout << things.at(2) << std::endl;
std::cout << t2 << std::endl;
}
//Output:
50
10101
50
10026
10026
These code snippets should give you a decent sense of how the language works, and what principles, possibly unfamiliar in the code I've written, that you need to learn about. My general recommendation is to find a good C++ resource for learning the basics of the language, and learn from that. Some good resources can be found here.
One last thing: if the use of Thing is critical to your code, because you need more data saved in the map, consider this instead:
#include<map>
#include<iostream>
#include<string>
//Only difference between struct and class is struct sets everything public by default
struct Thing {
int value;
double rate;
std::string name;
Thing() : Thing(0,0,"") {}
Thing(int value, double rate, std::string name) : value(value), rate(rate), name(std::move(name)) {}
};
int main() {
std::map<int, Thing> things;
Thing & t1 = things[1];
t1.value = 10;
t1.rate = 5.7;
t1.name = "First Object";
Thing & t2 = things[2];
t2.value = 15;
t2.rate = 17.99999;
t2.name = "Second Object";
t2.value++;
std::cout << things.at(2).value << std::endl;
t1.rate *= things.at(2).rate;
std::cout << things.at(1).rate << std::endl;
std::cout << t1.name << "," << things.at(2).name << std::endl;
things.at(1).rate -= 17;
std::cout << t1.rate << std::endl;
}
Based on what François Andrieux and Eljay have said (and what I would have said, had I got there first), here is the way I would do it, if you want to mutate objects you have already added to a list. All that reference_wrapper stuff is just a fancy way of passing pointers around. It will end in tears.
OK. here's the code (now edited as per OP's request):
#include <iostream>
#include <list>
#include <memory>
class Thing {
private:
int id;
int value = 0;
static int nextId;
public:
Thing() { this->id = Thing::nextId++; };
int getId() const { return this->id; };
int getValue() const { return this->value; };
void add(int n) { this->value += n; };
};
int Thing::nextId = 1;
class Storage {
private:
std::list<std::shared_ptr<Thing>> list;
public:
void add(const std::shared_ptr<Thing>& thing) {
this->list.push_back(thing);
}
std::shared_ptr<Thing> findById(int id) const {
for (std::list<std::shared_ptr<Thing>>::const_iterator it = this->list.begin(); it != this->list.end(); ++it) {
if (it->get()->getId() == id) return *it;
}
std::cout << "Not found!!\n";
exit(1);
}
};
void add_another(Storage& storage) {
storage.findById(2)->add(1);
std::shared_ptr<Thing> t4 = std::make_shared<Thing> (); t4->add(50);
storage.add(t4);
std::cout << storage.findById(4)->getValue() << "\n";
}
int main() {
std::shared_ptr<Thing> t1 = std::make_shared<Thing> (); t1->add(10);
std::shared_ptr<Thing> t2 = std::make_shared<Thing> (); t2->add(100);
std::shared_ptr<Thing> t3 = std::make_shared<Thing> (); t3->add(1000);
Storage storage;
storage.add(t3);
storage.add(t1);
storage.add(t2);
add_another(storage);
t2->add(10000);
std::cout << storage.findById(2)->getValue() << "\n";
std::cout << storage.findById(4)->getValue() << "\n";
return 0;
}
Output is now:
50
10101
50
as desired. Run it on Wandbox.
Note that what you are doing here, in effect, is reference counting your Things. The Things themselves are never copied and will go away when the last shared_ptr goes out of scope. Only the shared_ptrs are copied, and they are designed to be copied because that's their job. Doing things this way is almost as efficient as passing references (or wrapped references) around and far safer. When starting out, it's easy to forget that a reference is just a pointer in disguise.
Given that your Storage class does not own the Thing objects, and every Thing object is uniquely counted, why not just store Thing* in the list?
class Storage {
private:
std::list<Thing*> list;
public:
void add(Thing& thing) {
this->list.push_back(&thing);
}
Thing* findById(int id) const {
for (auto thing : this->list) {
if (thing->getId() == id) return thing;
}
std::cout << "Not found!!\n";
return nullptr;
}
};
EDIT: Note that Storage::findById now returns Thing* which allows it to fail gracefully by returning nullptr (rather than exit(1)).
This question already has answers here:
C++, variable declaration in 'if' expression
(8 answers)
Closed 3 years ago.
I was wondering if there was a way to put this on one line?
if (auto r = getGlobalObjectByName(word)) r->doSomething; // This works fine
if (!auto r = getGlobalObjectByName(word)) r->doSomething; // Says "expected an expression"
if (auto r = getGlobalObjectByName(word) == false) r->doSomething; // Also doesn't work.
I also tried surrounding it with extra brackets, but that doesn't seem to work. I find this really convenient doing it on one line.
Since C++17 you can use an initializer if-statement:
if (auto r = getGlobalObjectByName(word); !r) r->doSomething;
The semantics are:
if (init-statement; condition) statement
The only difference from the "traditional" if-statement is the init-statement, which initializes a variable in the block scope, similar to for-loops.
If you have C++17, use the if (init statement; condition) form. If not, you have three choices:
Stop trying to keep this all on one line. For example:
auto r = getGlobalObjectByName(word);
if (!r) r->doSomething();
Use an else:
if (auto r = getGlobalObjectByName(word)) {} else r->doSomething();
(Note that this requires that r is a smart pointer with very strange semantics for the operator bool() function. OTOH, I assume this is actual a short piece of example code, rather than your actual code).
I think I would only use the else form if it was really important to keep everything on one line (to preserve a tabular formatting of the code for example).
What you are trying to do is fine. By defining the variable inside the if you restrict its scope. That helps to reduce the number of stray variables after they have served their purpose.
Using this technique, if you want to follow the negative path, you need to use else like this:
if(auto r = getGlobalObjectByName(word))
{
r->doSomething();
}
else
{
// r == nullptr
// so do something else
}
There's also a way to do this with lambdas and C++14 but it does seem rather silly.
[](auto r){ if(!r)r->doSomething(); }(getGlobalObjectByName(word));
In C++11 you could also do this horrible mess (same idea, just no auto)
[](decltype(getGlobalObjectByName(word)) r){ if(!r)r->doSomething(); }(getGlobalObjectByName(word));
It is certainly no better than this clearer C++11 version mentioned by Martin Bonner:
{
auto r = getGlobalObjectByName(word);
if(!r)r->doSomething();
}
Here your code is clear that you want r to only be around for the duration of the if statement.
Before C++17 you can define a wrapper class, like here:
#include <utility>
template<typename T>
class NotT
{
T t;
public:
template<typename U>
NotT(U&& u) : t(std::move(u)) {}
explicit operator bool() const { return !t; }
T & value() { return t; }
T const& value() const { return t; }
};
template<typename T> NotT<T> Not(T&& t)
{
return NotT<T>(std::move(t));
}
#include <memory>
#include <iostream>
int main()
{
if(auto p=Not(std::make_shared<int>(2134)))
std::cout << "!p: p=" << p.value().get() << '\n';
else
std::cout << "!!p: p=" << p.value().get() << ", *p=" << *p.value() << '\n';
}
Live example
I'm writing a little event manager class where I store some function pointers inside a vector. I use std::function<void(int)> as vector type, I tested inserting inside it lambdas and normal functions and it works:
void t(int p){
/*things*/
}
[...]
event.bind([](int p){/*things*/});
event.bind(t);
Now, (at a certain point I need to delete lambdas but not functions,) my question is:
Is it possible to distinguish lambdas from functions? If yes, how?
EDIT:
Since I clarified my doubts, this question becomes just what the title says
The real answer is: you don't want to do this. It defeats the point of type-erasing functors if you actually want to know the original type also in case of whatever. This just smells like bad design.
What you are potentially looking for is std::function::target_type. This is a way to pull out the underlying type_info of the target function that the function object is storing. Each type_info has a name(), which can be demangled. Note that this is a very deep rabbit hole and you're basically going to have to hard-code all sorts of weird edge-cases. As I've been doing thanks to Yakk's very loving help.
Different compilers mangle their lambda names differently, so this approach doesn't even resemble portability. Quick checking shows that clang throws in a $ while gcc throws {lambda...#d}, So we can attempt to take advantage of that by writing something like:
bool is_identifier(std::string const& id) {
return id == "(anonymous namespace)" ||
(std::all_of(id.begin(), id.end(),
[](char c){
return isdigit(c) || isalpha(c) || c == '_';
}) && !isdigit(id[0]));
}
bool is_lambda(const std::type_info& info)
{
std::unique_ptr<char, decltype(&std::free)> own {
abi::__cxa_demangle(info.name(), nullptr, nullptr, nullptr),
std::free
};
std::string name = own ? own.get() : info.name();
// drop leading namespaces... if they are valid namespace names
std::size_t idx;
while ((idx = name.find("::")) != std::string::npos) {
if (!is_identifier(name.substr(0, idx))) {
return false;
}
else {
name = name.substr(idx+2);
}
}
#if defined(__clang__)
return name[0] == '$';
#elif defined(__GNUC__)
return name.find("{lambda") == 0;
#else
// I dunno?
return false;
#endif
}
And then throw that in your standard erase-remove idiom:
void foo(int ) { }
void bar(int ) { }
long quux(long x) { return x; }
int main()
{
std::vector<std::function<void(int)>> v;
v.push_back(foo);
v.push_back(bar);
v.push_back(quux);
v.push_back([](int i) { std::cout << i << '\n';});
std::cout << v.size() << std::endl; // prints 4
v.erase(
std::remove_if(
v.begin(),
v.end(),
[](std::function<void(int)> const& f){
return is_lambda(f.target_type());
}),
v.end()
);
std::cout << v.size() << std::endl; // prints 3
}
No, not in general.
A std::function<void(int)> can store a function pointer to any function that can be called by passing a single rvalue int. There are an infinite number of such signatures.
The type of a lambda is an unique anonymous class for each declaration. Two distinct lambdas do not share any type relationship.
You can determine of a std::function<void(int)> stores a variable of a specific type, but in both the function pointer and lambda case there is an unbounded number of different types that can be stored in the std::function to consider. And you can only test for "exactly equal to a type".
You can access the type id information, but there is no portable representation there, and generally using that information for anything other than identity matching (and related) or debugging is a bad idea.
Now, a restricted version of the question (can you tell if a std::function<void(int)> contains a function pointer of type void(*)(int)) is easy to solve. But in general, doing so remains a bad idea: first, because it is delicate (code far away from the point you use it, like a subtle change to the function signature, can break things), and second, inspecting and changing your behavior based on the type stored in a std::function should only be done in extreme corner cases (usually involving updating your code from using void* style callbacks to std::function style callbacks).
Be it a function pointer or lambda, it ends up as a std::function<void(int)> in the vector. It is then std::function<void(int)>'s responsibility to manage the function pointer or lambda, not yours. That means, you just remove the std::function<void(int)>s you want from the vector. The destructor of std::function<void(int)> knows how to do things right. In your case, that would be doing nothing with function pointers and invoking the destructor of lambdas. std::function<void(int)> enables you to treat different things in a nice and uniform way. Don't misuse it.
NOTE: This answer presupposes that there is a finite, distinct number of function signatures that may be assigned as event handlers. It assumes that assigning any-old function with the wrong signature is a mistake.
You can use std::function::target to determine which ones are the function pointers and by process of elimination figure out which ones must be the lambdas:
void func1(int) {}
void func2(double) {}
int main()
{
std::vector<std::function<void(int)>> events;
events.push_back(func1);
events.push_back([](int){});
events.push_back(func2);
for(auto& e: events)
{
if(e.target<void(*)(int)>())
std::cout << "funcion int" << '\n';
else if(e.target<void(*)(double)>())
std::cout << "funcion double" << '\n';
else
std::cout << "must be lambda" << '\n';
}
}
This works because std::function::target returns a null pointer if the parameter type doesn't match.
Single variable example:
void func(int) {}
int main()
{
std::function<void(int)> f = func;
if(f.target<void(*)(int)>())
std::cout << "not a lambda" << '\n';
}
I've been trying to fully digest the undo pattern demoed in Sean Parent's talk "Inheritance Is The Base Class of Evil". The talk covers a number of bases, including C++ move semantics, and the use of concepts to implement polymorphism instead of inheritance, but the delta-undo storage pattern is the one I've been trying to get my head around. Here is a working adaptation of the example Parent gave in his talk:
#include <iostream>
#include <memory>
#include <vector>
#include <assert.h>
using namespace std;
template <typename T>
void draw(const T& x, ostream& out, size_t position)
{
out << string(position, ' ') << x << endl;
}
class object_t {
public:
template <typename T>
object_t(T x) : self_(make_shared<model<T>>(move(x))) {}
friend void draw(const object_t& x, ostream& out, size_t position)
{ x.self_->draw_(out, position); }
private:
struct concept_t {
virtual ~concept_t() = default;
virtual void draw_(ostream&, size_t) const = 0;
};
template <typename T>
struct model : concept_t {
model(T x) : data_(move(x)) { }
void draw_(ostream& out, size_t position) const
{ draw(data_, out, position); }
T data_; };
shared_ptr<const concept_t> self_;
};
// The document itself is drawable
using document_t = vector<object_t>;
void draw(const document_t& x, ostream& out, size_t position)
{
out << string(position, ' ') << "<document>" << endl;
for (const auto& e : x) draw(e, out, position + 2);
out << string(position, ' ') << "</document>" << endl;
}
// An arbitrary class
class my_class_t {
/* ... */
};
void draw(const my_class_t&, ostream& out, size_t position)
{ out << string(position, ' ') << "my_class_t" << endl; }
// Undo management...
using history_t = vector<document_t>;
void commit(history_t& x) { assert(x.size()); x.push_back(x.back()); }
void undo(history_t& x) { assert(x.size()); x.pop_back(); }
document_t& current(history_t& x) { assert(x.size()); return x.back(); }
// Usage example.
int main(int argc, const char * argv[])
{
history_t h(1);
current(h).emplace_back(0);
current(h).emplace_back(string("Hello!"));
draw(current(h), cout, 0);
cout << "--------------------------" << endl;
commit(h);
current(h).emplace_back(current(h));
current(h).emplace_back(my_class_t());
current(h)[1] = string("World");
draw(current(h), cout, 0);
cout << "--------------------------" << endl;
undo(h);
draw(current(h), cout, 0);
return EXIT_SUCCESS;
}
Instead of tracking undo as a stack of commands which capture their before and after states, this pattern tracks undo states as a stack of "whole documents" where every entry is, in effect, a complete copy of the document. The trick of the pattern is that, storage/allocations are only incurred for the portions of the document which are different between each state, using some indirection and shared_ptr. Each "copy" only incurs the storage penalty for what's different between it and the prior state.
The pattern in Parent's example shows that the "current" document is completely mutable, but gets committed to the history when you call commit on the history. This pushes a "copy" of the current state onto the history.
In the abstract, I find this pattern compelling. The example Parent presents in this talk was clearly contrived primarily to demonstrate his points about concept-based polymorphism and move semantics. Relative to those, the undo pattern feels ancillary, although I think its role is to point out the value of value semantics.
In the example, the document "model" is just "a vector of objects conforming to the concept". That served it's purpose for the demo, but I'm finding it hard to extrapolate from "vector of concepts" to "real world, typed model." (Let's just say that for the purposes of this question the concept-based polymorphism is not relevant.) So, for instance, consider the following trivial model where the "document" is a company with some number of employees, each with a name, a salary, and a picture:
struct image {
uint8_t bitmapData[640 * 480 * 4];
};
struct employee {
string name;
double salary;
image picture;
};
struct company {
string name;
string bio;
vector<employee> employees;
};
The question I have is: How can I introduce the indirection necessary to get the storage sharing without losing the ability to interact with the model directly and simply? By simplicity of interaction, I mean that you should be able to continue to interact with the model in a straightforward manner without lots of RTTI or casting, etc. For instance, if you were trying to give everyone named "Susan" a 10% raise, capturing an undo state after every change, a simple interaction might look something like this:
using document_t = company;
using history_t = vector<document_t>;
void commit(history_t& x) { assert(x.size()); x.push_back(x.back()); }
void undo(history_t& x) { assert(x.size()); x.pop_back(); }
document_t& current(history_t& x) { assert(x.size()); return x.back(); }
void doStuff()
{
history_t h(1);
for (auto& e : current(h).employees)
{
if (e.name.find("Susan") == 0)
{
e.salary *= 1.1;
commit(h);
}
}
}
The trick seems to be injecting the indirection provided by object_t, but it's not clear how I can both introduce the necessary indirection and subsequently traverse that indirection transparently. I can generally get around in C++ code, but it's not my everyday language, so this could very well be something dead simple. Regardless, it's unsurprising that Parent's example doesn't cover this since a large part of his point was the ability to hide the type through the use of concepts.
Anyone have any thoughts on this?
While the document is mutable, the objects are not.
In order to edit an object, you need to create a new one.
In a practical solution, each object might be a cooy on write smart pointer holder that you can access either by reading or writing. A write access duplicates the object iff it has a reference count above one.
If you are willing to restrict all mutation to accessor methods, you can do the copy on write in them. If not, the get_writable method does the copy on write. Note that a modification usually implies a modification all the way back to the root as well, so your write method may need to take a path to the root where the copy on write is propagated up to there. Alternatively, you can use a document context and guid equivalent identifiers and a hash map, so editing foo contained in bar leaves bar unchanged, as it identifies foo by its name not a pointer.
I'm not sure how I should word this, so I'll attempt to put it in code. (This has many errors I know it will not compile it is simply to show what I want to do because I can't put it in words)
using namespace std; //for correctness sake
class foo {
public:
foo(int a=0, int n=0);
void stuff(int a);
void function(int n);
const int get_function() {return n;}
const int get_stuff(){return a;}
private:
int n, a;
};
struct Store{
public: get_foo(){return vector<f.foo>;} //I'm not too sure of the syntax here but you get the idea
private:
foo f;
}
Basically I want to take all the information that is returned in class foo, and output this, formatted, to a file. Thing is, I need to make many of these within the file and it has to be able to read it back for it to be worth anything.
So just appending each consecutive foo class to the file won't work(at least I don't see how).
I tried using ostream to overload the << operator, but I'm not sure how to call it to write it to the file. Any suggestions are welcome! Thanks.
I think your Store should be like this:
struct Store
{
public:
std::vector<foo> get_foo()
{
return f;
}
private:
std::vector<foo> f;
};
To overload << of std::ostream:
std::ostream& operator<<(std::ostream& out, const Store& f)
{
for (auto &x : f.get_foo())
out << x.get_function() << ", " << x.get_stuff() << "\n";
return out;
}
//Without auto
std::ostream& operator<<(std::ostream& out, const Store& f)
{
std::vector<foo> q = f;
for (int i=0; i<q.size(); i++)
out << q[i].get_function() << ", " << q[i].get_stuff() << "\n";
return out;
}
There are many tings wrong with your code.
So many that it's clear that you never read any C++ book and are just experimenting with a compiler.
Don't do that. C++ is really the worst language in the world to approach that way for many independent reasons.
No matter how smart you are.
Actually being smart is sort of a problem in certain areas because many C++ rules are not the result of a coherent logical design, but of historical evolution and committee decisions. Not even Hari Seldon would be able to foresee correctly what a committee would decide, you cannot deduce history.
Just pick a good book and read it cover to cover. There is no other sensible way to learn C++.
About writing structs to a file the topic is normally called "serialization" and takes care of the slightly more general problem of converting live objects into a dead sequence of bytes (written to a file or sent over the network) and the inverse problem "deserialization" of converting the sequence of bytes back into live objects (on the same system, on another identical system or even on a different system).
There are many facets of this problem, for example if your concern is about portability between systems, speed, size of the byte sequence, ability to reload bytes sequences that were saved back when your classes were slightly different because you evolved the program (versioning).
The simplest thing you can do is just fwrite things to a file, but this is most often simply nonsense in C++ and is a terrible way for many reasons even when it's technically possible. For example you cannot directly fwrite an std::vector object and hope to read it back.
I think you need something like this:
template<typename T>
std::ostream& operator << (std::ostream& out, const std::vector<T*>& elements)
{
for (size_t i = 0; i < elements.size(); i++)
out << elements[i] << ", ";
return out << std::endl;
}