Reputation: 23798
In C++, if I write
token make_token() { return token{}; }
and then use it as follows
void use_token()
{
make_token();
// extra code
}
without assigning a token to a variable, token
's destructor fires before extra code executes. How can I get the destructor to only fire at the end of the function without having to make a variable?
Note: I want to completely avoid making a variable. I know I can do auto& t = make_token()
or similar, but I want to avoid precisely this by returning something (I don't know what) that doesn't have the destructor fired immediately.
Why I want this: basically, in my app (a compiler for a programming language) I have these things called tokens. A token's constructor can put a {
and indent, and its destructor can then put }
and un-indent. I thought it a good idea to set up functions which return these tokens by value, but I don't actually want to assign them to any value, since the tokens are useless and have no functions.
To alleviate confusion, my token
is not a lexical token. I use the work token
in lieu of the work cookie
. It's meant to do something in the constructor, wait until the end of its scope, and then do something in its destructor. That's it. By the way, if I was writing this in C#, I would write something like
using (make_token())
{
// my code here
}
and it would work as intended. But it turns out that something so simple is difficult in C++.
Upvotes: 3
Views: 2344
Reputation: 44288
We have classical XY problem here:
So for C# code:
using (make_token())
{
// my code here
}
create a class token:
class token {
public:
token() { /* calling make_token(); */ }
~token() { /* destroying token */ }
};
then use it:
{
token tok;
// some stuff here
{
token tok;
// some other stuff here
}
}
So
If necessary you can put that into macro, but personaly I would find it more difficult to use.
Upvotes: 2
Reputation: 10880
I feel your pain, auto = make_token()
would be useful. However...
You might have the XY-problem. Shouldn't you do instead:
with_token([&]{ ... });
I.e., the token generator/constructor taking a lambda? This should work if you don't want to write return
within the lambda to return from the actual function creating the token.
Another approach is, if you just want no-one to 'know the name', the notorious for-pattern:
template<typename T>
struct Keeper
{
const T t;
char b;
Keeper(const T& t_)
: t(t_) {}
char* begin() { return &b; }
char* end() { return begin() + 1; }
};
template<typename T>
auto make_keeper(const T& t)
{
return Keeper<T>(t);
}
void f()
{
for (char c : make_keeper(make_token()))
{
// now try to name t or Keeper<T>(t) here
}
}
You might add move schematics and perfect fwd if you wish.
Upvotes: 0
Reputation: 3103
If you're able to use C++11 or later, you could write a template function something like:
template <typename T, typename Functor>
void keep_alive(T&&, Functor f) {
f();
}
...
void use_token() {
keep_alive(make_token(), [&] {
// rest of body of function
});
}
Edit after the clarification of why it's wanted:
For the specific use case of creating a token to put in { } and indent, you could create a wrapper function specifically named to make it clear what's happening:
template <typename Functor>
void make_indented_block(Functor f) {
auto indentToken = make_token();
f();
}
Upvotes: 4
Reputation: 55425
Like this:
make_token(),
[](){ /* extra stuff */ }();
Make sure you wash your hands afterwards :)
Upvotes: 8
Reputation: 21576
Well, you may think you can receive the value returned by that function;
void use_token()
{
auto nonsense = make_token();
// extra code
}
Even with this, did you know that (Pre-C++17) ... There was still possibility for two destructor calls there when RVO doesn't take place?
Taking it by const
reference as The Quantum Physicist's answer says is the best way out.
Upvotes: 1
Reputation: 26356
Yes. You can use a constant reference. This is called most important const in C++, and it's a feature that's not widely known.
Here's how you do it:
void use_token()
{
const token& myToken = make_token();
// now myToken is alive until the end of this function.
}
But you have to return strictly by value for this to work (you do that in the code you provided).
People who don't believe this, please try it yourself before attacking the post.
Upvotes: 8