-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
End oappend #4134
End oappend #4134
Conversation
Remove the large stack buffer as we're just going to copy it in to a heap buffer anyways. Later we can refine the length estimation or use a rope-style dynamic data structure like DynamicBufferList.
Since we validate the file existence ourselves, no need to have AsyncWebServer do it again.
Reduce the total number of calls by using printf_P and skipping atoi().
Useful for checking that I haven't broken anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the outcome. I even went ahead and checked AWS and the implementation of DynamicBuffer. Nice!
I do not find the code less readable but it sure is odd to see dest.print()
and sappend()
in the same context. Would it make sense to make a derived class and wrap sappend()
functionality into it to keep dest.print()
format?
wled00/util.cpp
Outdated
oappend(".checked="); | ||
oappendi(val); | ||
oappend(";"); | ||
type_str = F(".checked="); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use PSTR(...) and avoid casting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In an in-between version of the code, I had it set up so the print of that element was PROGMEM aware. Given that this version just passes it in to printf as a '%s', you're correct that F() adds no extra value here -- instead we'll just do the alignment fault dance.
Keeping something like |
I suppose the alternative would be to add some shim types, eg. #include <Printable.h>
class SetCheckbox : public Printable {
const char* _key; bool _val;
public:
inline SetCheckbox(const char* key, bool val) : _key(key), _val(val) {};
size_t printTo(Print& dest) override { return dest.printf_P(PSTR("d.Sf.%s.checked=%d;",_key,(int)_val); }
}
template<typename T>
class SetValueImpl : public Printable {
const char* _key; const T& _val;
public:
inline SetValueImpl(const char* key, const T& val) : _key(key), _val(val) {};
size_t printTo(Print& dest) override { return dest.printf_P(PSTR("d.Sf.%s.value=",_key) + dest.print(val) + dest.print(';'); }
}
// Factory for convenient usage syntax
template<typename T>
inline auto SetValue(const char* key, const T& value) { return SetValueImpl<T> {key, value} );
// Usage
{
dest.print(SetValue(PSTR("CM"), cmDNS));
dest.print(SetValue(PSTR("AB"), apBehavior));
dest.print(SetValue(PSTR("AS"), apSSID));
dest.print(SetCheckbox(PSTR("AH"), apHide));
} That said, this almost certainly would come at a code size and performance cost as the compiler can't see through the resolution of the Printable class to skip the vtable lookups, since the Arduino core folks didn't put the implementation of |
I am nowhere near the C++ knowledge to understand what you said but common logic says "if you use template doesn't that use base classes"? |
The template usage in that example was mostly because I dislike having to specify the types at the point of usage, eg, My specific complaint about the formulation using However, the Arduino core has put the logic that actually calls the virtual function in another translation unit (Print.cpp). So the compiler can't do any of that - if we used something like the sketch above, the compiler has no choice but to actually instantiate the object on the stack, and then pass a pointer in to All that said: it's also possible that link-time optimization offers the toolchain enough power to fix some of this, but I don't have a lot of experience with it, so my confidence level is low. |
Use named functions to describe what's being printed.
You act like "my wish is your command". 😄 |
You're the reviewer - it's my responsibility to shape the PR up to your standards! |
Ouch! |
Erp, sorry, didn't mean to offend! I think having high standards for code quality is a very good thing for a widely-used project, especially with embedded software where a mistake can make it really difficult to install a patch later. I guess maybe I'm just not so good at distinguishing between "wouldn't it be nice to have" and "please include in PR". Especially with a refactor like this, might as well get the most bang for the testing buck, I think.. |
Not offended. Just reminded me that I need to be careful how I phrase my sentences. I am bad at that. |
Pulled into test branch, let the fun begin. First sign is good!
|
Fount 1 issue:
causes issues as WLED_MAX_ANALOG_CHANNELS is now defined as
to use HAL. EDIT: A quick fix. dest.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d);"),
WLED_MAX_BUSSES,
WLED_MIN_VIRTUAL_BUSSES,
MAX_LEDS_PER_BUS,
MAX_LED_MEMORY,
MAX_LEDS,
WLED_MAX_COLOR_ORDER_MAPPINGS,
WLED_MAX_DIGITAL_CHANNELS,
WLED_MAX_ANALOG_CHANNELS
); |
Yeah, I went back and forth on the use of TOSTRING. When it works, it saves both code size and runtime performance, but it won't work for all legal macros. I wish there was a compile-time printf() - maybe someday constexpr std::format will give it to me. |
Macros aren't type-safe; this has already caused one merge failure.
@softhack007 would you care to take a look? another pair of eyes is nothing but beneficial. |
@blazoncek yeah i've just "landed" after having a week full of meetings with customers. Once my head has cooled down a bit, i'll take a look. |
First thought: it's probably the right approach, but it means that all usermods need to be touched - and some of them (audioreactive) do a lot of oappend(). Would it be possible to still leave a legacy "oappend()" that's wrapping the new functionality?
|
If you're happy that changing the usermod API is a good long-term plan, I can build a compatibility shim specifically in the context of usermods to ease the transition process. I'll sketch something tonight if I can. |
Use a static Print* to transform old oappend calls to print calls.
OK, I've put in a shim so that existing The followup question: this PR includes a search-and-replace on existing usermods to update them to the new API. I've tried a couple and they seem to work, but I haven't tested everything. Should I revert that? Or just for certain usermods where there's particular value in staying close to their own upstream? |
oappend(SET_F("');")); | ||
oappend(SET_F("addOption(dd,'0x76',0x76);")); | ||
oappend(SET_F("addOption(dd,'0x77',0x77);")); | ||
dest.print(F("dd=addDropdown('")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idea: while "dest" is technicially correct, it might be better to find a name that describes the purpose of dest
.
How about settings.print(....)
or uiScript.print(...)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dest is just a reference to Print object. There can only be one, but can be named whatever you like.
That's in the function definition above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a second thought, why not just oappend.print(...);
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. My preference would be settingsScript
- I think might as well take the opportunity to use a clearly purposed name. Of course as @blazoncek points out, whatever we pick would be merely a convention - usermod authors are free to name it whatever they like.
👍
Good question .... maybe we revert all usermods to the old |
Well. 😄 |
I'm closing this one in favor of #4152, which has the same patch sequence but with no usermod changes. We can table those as a separate PR. |
Replace the usage of
oppend
with Arduino'sPrint
abstraction. This permits efficient use of dynamically allocated memory for returning settings page data, without requiring a single large contiguous allocation up front, or risking smashing the stack.This change also permits the output generation functions to easily use more sophisticated serialization functions such as
printf
, reducing the number of required function calls. With this optimization, the overall result is approximate parity in code size to the original implementation. This has unfortunately come at a cost in code readability.NOTE - this is a BREAKING CHANGE to the Usermod API!
Also note: to get the full benefits from ths PR, the AsyncWebServer's
AsyncStreamResponse
needs a better allocation implementation than it currently has. This improvement has not yet been merged upstream.