working with expressions: how to minimize runtime construction time
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
I have two classes, a single expression (SE
) and a bundle of two expressions (ME
). The bundle is an expression itself, hence it can be an element of another bundle.
struct SE {
SE(char id, char n) : id(id), n(n) {}
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
char id, n;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r) : lhs(l), rhs(r) { }
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
LHS lhs;
SE rhs;
};
The construction of the bundle performs a simple validity check based on the data member n
, accessible in ME
via the method size
. An eval
method does some claculations using the data member id
. Neither n
nor id
are known at compile time.
For both classes I override the comma operator, so that it performs the recursive bundling of multiple single expression into a nested bundle.
auto SE::operator,(const SE& r) { return ME<SE>(*this, r); }
auto ME<LHS>::operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
I want that, after the whole bundle has been constructed, the method eval
is triggered on the whole bundle. Example:
SE('a',1); // prints 'a'
SE('a',1), SE('b',1); // prints '(a,b)'
SE('a',1), SE('b',1), SE('c',1); // prints '((a,b),c)'
A possible way to achieve that is to use the destructors of the classes and add a flag is_outer
which is updated appropriately during contruction of SE
and ME
. When any of these class is destructed, if the flag indicates this is the outermost class, then eval
is triggered. A full demo is given below.
Testing on godbolt the simple demo
function below, it seems to me the compiler generates more code than strictly necessary. Although id
and n
are not known at compile time, the final type of the expression should be. I would expect the entire construction of the bundle to reduce to just moving a few numbers in the correct place, then check the assertions, but it seems to actually do much more copies.
Is it possible to obtain that more of the contruction part is produced at compile time?
#include <iostream>
#include <cassert>
#include <string>
#include <sstream>
using namespace std;
// forward declaration
template <typename LHS> struct ME;
struct SE {
SE(char id, char n) : id(id), n(n), outer(true) {}
SE(const SE& expr) : id(expr.id), n(expr.n), outer(false) {}
ME<SE> operator,(const SE& r);
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
~SE() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='n'; cout << b; } }
char id, n;
mutable bool outer;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r)
: lhs(l), rhs(r), outer(true) // tentatively set to true
{ l.outer = r.outer = false; assert(l.size() == r.size()); } // reset flag for arguments
ME(const ME<LHS>& expr)
: lhs(expr.lhs), rhs(expr.rhs), outer(false) {}
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
auto operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
~ME() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='n'; cout << b; } }
LHS lhs;
SE rhs;
mutable bool outer;
};
ME<SE> SE::operator,(const SE& r) { return ME<SE>(*this, r); }
void demo(char a, char na, char b, char nb, char c, char nc) {
SE(a, na), SE(b,nb), SE(c,nc); // prints '((a,b),c)'
}
int main() {
demo('a',1,'b',1,'c',1);
return 0;
}
c++ c++11 operator-overloading
add a comment |
I have two classes, a single expression (SE
) and a bundle of two expressions (ME
). The bundle is an expression itself, hence it can be an element of another bundle.
struct SE {
SE(char id, char n) : id(id), n(n) {}
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
char id, n;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r) : lhs(l), rhs(r) { }
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
LHS lhs;
SE rhs;
};
The construction of the bundle performs a simple validity check based on the data member n
, accessible in ME
via the method size
. An eval
method does some claculations using the data member id
. Neither n
nor id
are known at compile time.
For both classes I override the comma operator, so that it performs the recursive bundling of multiple single expression into a nested bundle.
auto SE::operator,(const SE& r) { return ME<SE>(*this, r); }
auto ME<LHS>::operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
I want that, after the whole bundle has been constructed, the method eval
is triggered on the whole bundle. Example:
SE('a',1); // prints 'a'
SE('a',1), SE('b',1); // prints '(a,b)'
SE('a',1), SE('b',1), SE('c',1); // prints '((a,b),c)'
A possible way to achieve that is to use the destructors of the classes and add a flag is_outer
which is updated appropriately during contruction of SE
and ME
. When any of these class is destructed, if the flag indicates this is the outermost class, then eval
is triggered. A full demo is given below.
Testing on godbolt the simple demo
function below, it seems to me the compiler generates more code than strictly necessary. Although id
and n
are not known at compile time, the final type of the expression should be. I would expect the entire construction of the bundle to reduce to just moving a few numbers in the correct place, then check the assertions, but it seems to actually do much more copies.
Is it possible to obtain that more of the contruction part is produced at compile time?
#include <iostream>
#include <cassert>
#include <string>
#include <sstream>
using namespace std;
// forward declaration
template <typename LHS> struct ME;
struct SE {
SE(char id, char n) : id(id), n(n), outer(true) {}
SE(const SE& expr) : id(expr.id), n(expr.n), outer(false) {}
ME<SE> operator,(const SE& r);
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
~SE() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='n'; cout << b; } }
char id, n;
mutable bool outer;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r)
: lhs(l), rhs(r), outer(true) // tentatively set to true
{ l.outer = r.outer = false; assert(l.size() == r.size()); } // reset flag for arguments
ME(const ME<LHS>& expr)
: lhs(expr.lhs), rhs(expr.rhs), outer(false) {}
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
auto operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
~ME() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='n'; cout << b; } }
LHS lhs;
SE rhs;
mutable bool outer;
};
ME<SE> SE::operator,(const SE& r) { return ME<SE>(*this, r); }
void demo(char a, char na, char b, char nb, char c, char nc) {
SE(a, na), SE(b,nb), SE(c,nc); // prints '((a,b),c)'
}
int main() {
demo('a',1,'b',1,'c',1);
return 0;
}
c++ c++11 operator-overloading
3
For working code, codereview.stackexchange.com seems more appropriate.
– Jarod42
Dec 28 '18 at 10:39
"How to determine if the class is the outermost or not?": you cannot, but you can determine if a class is not the outermost (the constr ofME
can alert (inhibit) thoseAssignExpr
they are not the outermost). Your solution is then to trigger evaluation if a class has not received an inhibiting signal.
– YSC
Dec 28 '18 at 10:41
And this is what you do. See Jarod's comment then ;)
– YSC
Dec 28 '18 at 10:43
add a comment |
I have two classes, a single expression (SE
) and a bundle of two expressions (ME
). The bundle is an expression itself, hence it can be an element of another bundle.
struct SE {
SE(char id, char n) : id(id), n(n) {}
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
char id, n;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r) : lhs(l), rhs(r) { }
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
LHS lhs;
SE rhs;
};
The construction of the bundle performs a simple validity check based on the data member n
, accessible in ME
via the method size
. An eval
method does some claculations using the data member id
. Neither n
nor id
are known at compile time.
For both classes I override the comma operator, so that it performs the recursive bundling of multiple single expression into a nested bundle.
auto SE::operator,(const SE& r) { return ME<SE>(*this, r); }
auto ME<LHS>::operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
I want that, after the whole bundle has been constructed, the method eval
is triggered on the whole bundle. Example:
SE('a',1); // prints 'a'
SE('a',1), SE('b',1); // prints '(a,b)'
SE('a',1), SE('b',1), SE('c',1); // prints '((a,b),c)'
A possible way to achieve that is to use the destructors of the classes and add a flag is_outer
which is updated appropriately during contruction of SE
and ME
. When any of these class is destructed, if the flag indicates this is the outermost class, then eval
is triggered. A full demo is given below.
Testing on godbolt the simple demo
function below, it seems to me the compiler generates more code than strictly necessary. Although id
and n
are not known at compile time, the final type of the expression should be. I would expect the entire construction of the bundle to reduce to just moving a few numbers in the correct place, then check the assertions, but it seems to actually do much more copies.
Is it possible to obtain that more of the contruction part is produced at compile time?
#include <iostream>
#include <cassert>
#include <string>
#include <sstream>
using namespace std;
// forward declaration
template <typename LHS> struct ME;
struct SE {
SE(char id, char n) : id(id), n(n), outer(true) {}
SE(const SE& expr) : id(expr.id), n(expr.n), outer(false) {}
ME<SE> operator,(const SE& r);
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
~SE() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='n'; cout << b; } }
char id, n;
mutable bool outer;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r)
: lhs(l), rhs(r), outer(true) // tentatively set to true
{ l.outer = r.outer = false; assert(l.size() == r.size()); } // reset flag for arguments
ME(const ME<LHS>& expr)
: lhs(expr.lhs), rhs(expr.rhs), outer(false) {}
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
auto operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
~ME() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='n'; cout << b; } }
LHS lhs;
SE rhs;
mutable bool outer;
};
ME<SE> SE::operator,(const SE& r) { return ME<SE>(*this, r); }
void demo(char a, char na, char b, char nb, char c, char nc) {
SE(a, na), SE(b,nb), SE(c,nc); // prints '((a,b),c)'
}
int main() {
demo('a',1,'b',1,'c',1);
return 0;
}
c++ c++11 operator-overloading
I have two classes, a single expression (SE
) and a bundle of two expressions (ME
). The bundle is an expression itself, hence it can be an element of another bundle.
struct SE {
SE(char id, char n) : id(id), n(n) {}
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
char id, n;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r) : lhs(l), rhs(r) { }
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
LHS lhs;
SE rhs;
};
The construction of the bundle performs a simple validity check based on the data member n
, accessible in ME
via the method size
. An eval
method does some claculations using the data member id
. Neither n
nor id
are known at compile time.
For both classes I override the comma operator, so that it performs the recursive bundling of multiple single expression into a nested bundle.
auto SE::operator,(const SE& r) { return ME<SE>(*this, r); }
auto ME<LHS>::operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
I want that, after the whole bundle has been constructed, the method eval
is triggered on the whole bundle. Example:
SE('a',1); // prints 'a'
SE('a',1), SE('b',1); // prints '(a,b)'
SE('a',1), SE('b',1), SE('c',1); // prints '((a,b),c)'
A possible way to achieve that is to use the destructors of the classes and add a flag is_outer
which is updated appropriately during contruction of SE
and ME
. When any of these class is destructed, if the flag indicates this is the outermost class, then eval
is triggered. A full demo is given below.
Testing on godbolt the simple demo
function below, it seems to me the compiler generates more code than strictly necessary. Although id
and n
are not known at compile time, the final type of the expression should be. I would expect the entire construction of the bundle to reduce to just moving a few numbers in the correct place, then check the assertions, but it seems to actually do much more copies.
Is it possible to obtain that more of the contruction part is produced at compile time?
#include <iostream>
#include <cassert>
#include <string>
#include <sstream>
using namespace std;
// forward declaration
template <typename LHS> struct ME;
struct SE {
SE(char id, char n) : id(id), n(n), outer(true) {}
SE(const SE& expr) : id(expr.id), n(expr.n), outer(false) {}
ME<SE> operator,(const SE& r);
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
~SE() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='n'; cout << b; } }
char id, n;
mutable bool outer;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r)
: lhs(l), rhs(r), outer(true) // tentatively set to true
{ l.outer = r.outer = false; assert(l.size() == r.size()); } // reset flag for arguments
ME(const ME<LHS>& expr)
: lhs(expr.lhs), rhs(expr.rhs), outer(false) {}
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
auto operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
~ME() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='n'; cout << b; } }
LHS lhs;
SE rhs;
mutable bool outer;
};
ME<SE> SE::operator,(const SE& r) { return ME<SE>(*this, r); }
void demo(char a, char na, char b, char nb, char c, char nc) {
SE(a, na), SE(b,nb), SE(c,nc); // prints '((a,b),c)'
}
int main() {
demo('a',1,'b',1,'c',1);
return 0;
}
c++ c++11 operator-overloading
c++ c++11 operator-overloading
edited Jan 3 at 6:21
Fabio
asked Dec 28 '18 at 10:31
FabioFabio
971419
971419
3
For working code, codereview.stackexchange.com seems more appropriate.
– Jarod42
Dec 28 '18 at 10:39
"How to determine if the class is the outermost or not?": you cannot, but you can determine if a class is not the outermost (the constr ofME
can alert (inhibit) thoseAssignExpr
they are not the outermost). Your solution is then to trigger evaluation if a class has not received an inhibiting signal.
– YSC
Dec 28 '18 at 10:41
And this is what you do. See Jarod's comment then ;)
– YSC
Dec 28 '18 at 10:43
add a comment |
3
For working code, codereview.stackexchange.com seems more appropriate.
– Jarod42
Dec 28 '18 at 10:39
"How to determine if the class is the outermost or not?": you cannot, but you can determine if a class is not the outermost (the constr ofME
can alert (inhibit) thoseAssignExpr
they are not the outermost). Your solution is then to trigger evaluation if a class has not received an inhibiting signal.
– YSC
Dec 28 '18 at 10:41
And this is what you do. See Jarod's comment then ;)
– YSC
Dec 28 '18 at 10:43
3
3
For working code, codereview.stackexchange.com seems more appropriate.
– Jarod42
Dec 28 '18 at 10:39
For working code, codereview.stackexchange.com seems more appropriate.
– Jarod42
Dec 28 '18 at 10:39
"How to determine if the class is the outermost or not?": you cannot, but you can determine if a class is not the outermost (the constr of
ME
can alert (inhibit) those AssignExpr
they are not the outermost). Your solution is then to trigger evaluation if a class has not received an inhibiting signal.– YSC
Dec 28 '18 at 10:41
"How to determine if the class is the outermost or not?": you cannot, but you can determine if a class is not the outermost (the constr of
ME
can alert (inhibit) those AssignExpr
they are not the outermost). Your solution is then to trigger evaluation if a class has not received an inhibiting signal.– YSC
Dec 28 '18 at 10:41
And this is what you do. See Jarod's comment then ;)
– YSC
Dec 28 '18 at 10:43
And this is what you do. See Jarod's comment then ;)
– YSC
Dec 28 '18 at 10:43
add a comment |
1 Answer
1
active
oldest
votes
The general pattern you are following is expression templates. Reading up on how others do it will help.
Usually expression templates use CRTP heavily, and do not store copies.
I believe I see bugs due to the copies.
Generally take T&&
and store T&
or T&&
.
Usually expression templates terminate (and execute) when they are assigned to a target; you don't want to that. As C++ lacks move-from-and-destroy, you have to check the "should not be executed" at (nominally) runtime.
Instead of references/values and a bool, you could store pointers and use null as the "don't run" case.
I cannot figure out how to make the work to determine what to run constexpr
. It might be possible however.
Hi. I simplified a lot the question and the source code. Indeed my final objective is to achieve that the runtime construction of the final expression is as fast as it could be. It seems to me that your answer touches the right topics, but it is too high level for me to make good use of it. Would you mind elaborating? Thank you.
– Fabio
Jan 3 at 6:09
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53957069%2fworking-with-expressions-how-to-minimize-runtime-construction-time%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
The general pattern you are following is expression templates. Reading up on how others do it will help.
Usually expression templates use CRTP heavily, and do not store copies.
I believe I see bugs due to the copies.
Generally take T&&
and store T&
or T&&
.
Usually expression templates terminate (and execute) when they are assigned to a target; you don't want to that. As C++ lacks move-from-and-destroy, you have to check the "should not be executed" at (nominally) runtime.
Instead of references/values and a bool, you could store pointers and use null as the "don't run" case.
I cannot figure out how to make the work to determine what to run constexpr
. It might be possible however.
Hi. I simplified a lot the question and the source code. Indeed my final objective is to achieve that the runtime construction of the final expression is as fast as it could be. It seems to me that your answer touches the right topics, but it is too high level for me to make good use of it. Would you mind elaborating? Thank you.
– Fabio
Jan 3 at 6:09
add a comment |
The general pattern you are following is expression templates. Reading up on how others do it will help.
Usually expression templates use CRTP heavily, and do not store copies.
I believe I see bugs due to the copies.
Generally take T&&
and store T&
or T&&
.
Usually expression templates terminate (and execute) when they are assigned to a target; you don't want to that. As C++ lacks move-from-and-destroy, you have to check the "should not be executed" at (nominally) runtime.
Instead of references/values and a bool, you could store pointers and use null as the "don't run" case.
I cannot figure out how to make the work to determine what to run constexpr
. It might be possible however.
Hi. I simplified a lot the question and the source code. Indeed my final objective is to achieve that the runtime construction of the final expression is as fast as it could be. It seems to me that your answer touches the right topics, but it is too high level for me to make good use of it. Would you mind elaborating? Thank you.
– Fabio
Jan 3 at 6:09
add a comment |
The general pattern you are following is expression templates. Reading up on how others do it will help.
Usually expression templates use CRTP heavily, and do not store copies.
I believe I see bugs due to the copies.
Generally take T&&
and store T&
or T&&
.
Usually expression templates terminate (and execute) when they are assigned to a target; you don't want to that. As C++ lacks move-from-and-destroy, you have to check the "should not be executed" at (nominally) runtime.
Instead of references/values and a bool, you could store pointers and use null as the "don't run" case.
I cannot figure out how to make the work to determine what to run constexpr
. It might be possible however.
The general pattern you are following is expression templates. Reading up on how others do it will help.
Usually expression templates use CRTP heavily, and do not store copies.
I believe I see bugs due to the copies.
Generally take T&&
and store T&
or T&&
.
Usually expression templates terminate (and execute) when they are assigned to a target; you don't want to that. As C++ lacks move-from-and-destroy, you have to check the "should not be executed" at (nominally) runtime.
Instead of references/values and a bool, you could store pointers and use null as the "don't run" case.
I cannot figure out how to make the work to determine what to run constexpr
. It might be possible however.
answered Jan 1 at 18:53
Yakk - Adam NevraumontYakk - Adam Nevraumont
189k21199384
189k21199384
Hi. I simplified a lot the question and the source code. Indeed my final objective is to achieve that the runtime construction of the final expression is as fast as it could be. It seems to me that your answer touches the right topics, but it is too high level for me to make good use of it. Would you mind elaborating? Thank you.
– Fabio
Jan 3 at 6:09
add a comment |
Hi. I simplified a lot the question and the source code. Indeed my final objective is to achieve that the runtime construction of the final expression is as fast as it could be. It seems to me that your answer touches the right topics, but it is too high level for me to make good use of it. Would you mind elaborating? Thank you.
– Fabio
Jan 3 at 6:09
Hi. I simplified a lot the question and the source code. Indeed my final objective is to achieve that the runtime construction of the final expression is as fast as it could be. It seems to me that your answer touches the right topics, but it is too high level for me to make good use of it. Would you mind elaborating? Thank you.
– Fabio
Jan 3 at 6:09
Hi. I simplified a lot the question and the source code. Indeed my final objective is to achieve that the runtime construction of the final expression is as fast as it could be. It seems to me that your answer touches the right topics, but it is too high level for me to make good use of it. Would you mind elaborating? Thank you.
– Fabio
Jan 3 at 6:09
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53957069%2fworking-with-expressions-how-to-minimize-runtime-construction-time%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
3
For working code, codereview.stackexchange.com seems more appropriate.
– Jarod42
Dec 28 '18 at 10:39
"How to determine if the class is the outermost or not?": you cannot, but you can determine if a class is not the outermost (the constr of
ME
can alert (inhibit) thoseAssignExpr
they are not the outermost). Your solution is then to trigger evaluation if a class has not received an inhibiting signal.– YSC
Dec 28 '18 at 10:41
And this is what you do. See Jarod's comment then ;)
– YSC
Dec 28 '18 at 10:43