-
Notifications
You must be signed in to change notification settings - Fork 193
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
[Developer] Adjust alfalfa_utility #5259
Conversation
@@ -2,8 +2,7 @@ | |||
#define ALFALFA_COMPONENT_ACTUATOR_HPP | |||
|
|||
#include "AlfalfaComponent.hpp" | |||
#include "utilities/idf/IdfObject.hpp" | |||
#include "utilities/core/Logger.hpp" |
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.
Logger was included everywhere but not used. Because you throw std::runtime_error.
Maybe you should use a proper Logger and use LOG_AND_THROW, one of the two.
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'm not sure what you mean by "use a proper Logger and use LOG_AND_THROW, one of the two" do you mean or? Can you provide an example of somewhere in the OS code where the pattern you'd like me to use is?
|
||
AlfalfaJSON_Impl::AlfalfaJSON_Impl(const std::string& s) {} | ||
AlfalfaJSON_Impl::AlfalfaJSON_Impl(const std::string& s) { | ||
// TODO: use s |
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.
TODO
|
||
AlfalfaJSON_Impl::AlfalfaJSON_Impl(const openstudio::path& p) {} | ||
AlfalfaJSON_Impl::AlfalfaJSON_Impl(const openstudio::path& p) { | ||
// TODO: use p |
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.
TODO
@@ -17,10 +17,13 @@ namespace alfalfa { | |||
AlfalfaPoint_Impl(); | |||
|
|||
protected: | |||
// TODO: is this pImpl is needed? If so, add getters and setters and make all members private. |
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.
TODO
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.
Sounds good, I was talking to Kyle about this. I feel like it is a bit more messy to have every getter and setter written twice, but since that seems to be the pattern here there's probably something I'm missing.
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.
It's annoying to write for sure. For ModelObjects we actually have ruby scripts that will do 90% of the work for us and generate the pimpl class: https://github.com/NREL/OpenStudio/blob/develop/developer/ruby/GenerateClass.rb and https://github.com/NREL/OpenStudio/blob/develop/developer/doc/Adding_Model_Object_to_OpenStudio.docx
I had written this when I first touched the OpenStudio codebase: https://github.com/jmarrec/OpenStudio-Tutorial-for-Cpp-Noobs?tab=readme-ov-file#pimpl
a few years later, I think the pImpl pattern in the case of model objects at least is useful for us because:
- The Impl methods are not exposed to SWIG (ruby, python, C# bindings)
- Technically you can change the Impl header without changing the public header, which is good because:
- If a class never included the Impl header (and we have to do that too often for casting especially), then there's a net compilation time benefit
- It forces us to treat the public header as a sacred API, and non-backward compatible changes must be really motivated. We often remove methods from the Impl when they become moot, and we'll put a
[[deprecated]]
method in the public side for 2 or 3 releases with a clear message that it'll be removed.
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.
That makes sense. The reason I used the pImpl pattern here was to allow the point object a user is given after creation and the point object in the points vector within AlflalfaJSON
to all point to the same underlying data. This seemed like the best option outside of dealing with just raw or lightly wrapped pointers. If there is a better paradigm you have in mind for this I would be happy to implement it.
@@ -100,7 +99,7 @@ namespace alfalfa { | |||
std::vector<AlfalfaPoint> getPoints(); | |||
|
|||
private: | |||
boost::optional<std::string> getName(const openstudio::IdfObject& idf_object); | |||
static boost::optional<std::string> getName(const openstudio::IdfObject& idf_object); |
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.
static a few methods
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.
Thanks for this @jmarrec just a couple questions if you have time.
} | ||
] | ||
} | ||
)json"; |
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.
Nice, what is the json(...)json
a directive to?
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.
Nothing, it's an optional arbitrary string, at most 16 characters long. I like to use it, when this is a json thing I put json, when it's a sql query I put sql... etc.
(technically speaking you could configure your editor to pick that up and apply a different syntax highlighting based on it)
https://en.cppreference.com/w/cpp/language/string_literal
R"d-char-seq (optional)(r-char-seq (optional))d-char-seq (optional)"
d-char-seq - A sequence of one or more d-char s, at most 16 characters long
d-char - A character from the basic character set, except parentheses, backslash and spaces
#include "utilities/idd/IddObject.hpp" | ||
#include "utilities/idd/IddEnums.hpp" | ||
#include "../idd/IddObject.hpp" | ||
#include "../idd/IddEnums.hpp" |
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.
So the convention is to use relative paths for project imports? I was opting for absolute as it doesn't break everything when you move files around.
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.
That's the way all other files are as far as I can tell. It's debatable whether it's a good idea, but I'd prefer consistency.
(Bit of context: We don't move things often (if at all), and I am not afraid of spending 15 minutes to write a python script with a regex or two to fixup the paths if we need to move things)
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.
that sounds good, currently there a question on the other PR from kyle as to whether alfalfa
should be moved out of utilities
to be on the same level. Ideally I think I'd wait for that before switching everything back to relative, but as you noted it's not that big of a lift to fix.
component_name = actuator.getString(OS_EnergyManagementSystem_ActuatorFields::ActuatedComponentName); | ||
component_type = actuator.getString(OS_EnergyManagementSystem_ActuatorFields::ActuatedComponentType); | ||
control_type = actuator.getString(OS_EnergyManagementSystem_ActuatorFields::ActuatedComponentControlType); | ||
component_name = actuator.getString(OS_EnergyManagementSystem_ActuatorFields::ActuatedComponentName).value_or(""); |
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.
didn't know about this pattern, seems like a good improvement.
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.
@@ -2,8 +2,7 @@ | |||
#define ALFALFA_COMPONENT_ACTUATOR_HPP | |||
|
|||
#include "AlfalfaComponent.hpp" | |||
#include "utilities/idf/IdfObject.hpp" | |||
#include "utilities/core/Logger.hpp" |
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'm not sure what you mean by "use a proper Logger and use LOG_AND_THROW, one of the two" do you mean or? Can you provide an example of somewhere in the OS code where the pattern you'd like me to use is?
|
||
namespace openstudio { | ||
namespace alfalfa { | ||
|
||
AlfalfaComponent::AlfalfaComponent(const std::string& type, const Capability capabilities) : type(type), capabilities(capabilities) {} | ||
AlfalfaComponent::AlfalfaComponent(const std::string& type, Capability capabilities) : type(type), capabilities(capabilities) {} |
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.
this is fair, since it's just a uint under the hood
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.
yes, it's a POD type [1] that'll fit in a register. I tried adding a clang-tidy check for that in LLVM (and someone else did independently) but it never got in.
[1] This may be an unsigned int, and it is on GCC for eg (cf https://gcc.godbolt.org/z/47z5z3bP8), but it's actually implementation defined per the cpp standards.
@@ -97,36 +103,36 @@ namespace alfalfa { | |||
Json::Value AlfalfaPoint::toJSON() const { | |||
Json::Value point; | |||
|
|||
if (id().is_initialized()) { | |||
point["id"] = id().get(); | |||
if (auto id_ = id()) { |
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.
oh cool, I didn't realize you can do assignment in the if
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.
boost::optional has an operator bool()
, which is going to be used here.
FYI, C++17 added init-statement to if
. So you could do something like
if (auto x = func(); x.condition()) {
namespace openstudio { | ||
namespace alfalfa { | ||
|
||
static constexpr std::string_view ID_VALID_CHARS_MSG = "IDs can only contain letters, numbers, and the following special characters _-[]():"; |
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.
That makes sense to move this out of the class.
Let me make sure I'm understanding this correctly. The purpose of using a constexpr std::string_view
is that this gets populated in memory and returns a pointer at compile time so that each time the string is accessed it doesn't need to build a new one?
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.
this is a global static constexpr variable. I could omit the static
, and it'd still be static
because it's declared at global scope and it's constexpr, but this is more explicit.
And so it initializes it in static storage (the .rodata
section of the binary). The string_view is materialized in the binary.
see this short example: https://gcc.godbolt.org/z/o7veTMETP
and if you want to learn more about constexpr, I suggest C++ weekly, the start of this episode maybe: https://www.youtube.com/watch?v=IDQ0ng8RIqs
@@ -17,10 +17,13 @@ namespace alfalfa { | |||
AlfalfaPoint_Impl(); | |||
|
|||
protected: | |||
// TODO: is this pImpl is needed? If so, add getters and setters and make all members private. |
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.
Sounds good, I was talking to Kyle about this. I feel like it is a bit more messy to have every getter and setter written twice, but since that seems to be the pattern here there's probably something I'm missing.
|
||
boost::optional<AlfalfaPoint> AlfalfaJSON::exposeConstant(float value, const std::string& display_name) { | ||
AlfalfaConstant component(value); | ||
return exposeFromComponent(component, display_name); | ||
return exposeFromComponent(AlfalfaConstant{value}, display_name); |
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'm not familiar with this method of constructing, this is just a way to construct the class inline in the stack?
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.
This is by definition a temporary (because it does not have a name), so it's an R-value (a prvalue
I think), and I know that there will be no copies.
In this specific case, this is unnecessary, because the exposeFromComponent
actually takes a const-ref, so no copy or move will happen, but that may change... It didn't hinder readability much here, so I just went with it.
This is heavily related to the pass-by-value and move idiom.
Here is something to play for you, switch the defines on line 41 and 42: https://gcc.godbolt.org/z/911xK1bT6
You'll see that if exposeFromComponent takes by-value:
- If you NAME your variable, the Ctor is called, then the Copy Ctor
- If you use a temporary (like I did here), the variable is actually constructed directly in the exposeFromComponent scope and you save a copy.
@TShapinsky I took some time to give (probably too) detailed explanations in the hope that it's useful context and references. Anyways, you're responsible for the PR / branch I'm pointing this to, so it's up to you to merge this one or not (I don't know if you have merge rights though, but I can click the button if you ask me to) |
Pull request overview
Pull Request Author
src/model/test
)src/energyplus/Test
)src/osversion/VersionTranslator.cpp
)Labels:
IDDChange
APIChange
Pull Request - Ready for CI
so that CI builds your PRReview Checklist
This will not be exhaustively relevant to every PR.