-
Notifications
You must be signed in to change notification settings - Fork 13
Implement CString as a wrapper around a C-String to avoid using std::string #107
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| #pragma once | ||
|
|
||
| #include <cassert> | ||
| #include <cstring> | ||
|
|
||
| namespace scorepy | ||
| { | ||
|
|
||
| /// Thin wrapper around a C-String (NULL-terminated sequence of chars) | ||
| class CString | ||
| { | ||
| const char* s_; | ||
| size_t len_; | ||
|
|
||
| public: | ||
| template <size_t N> | ||
| constexpr CString(const char (&s)[N]) : s_(s), len_(N - 1u) | ||
| { | ||
| static_assert(N > 0, "Cannot handle empty char array"); | ||
| } | ||
|
|
||
| explicit CString(const char* s, size_t len) : s_(s), len_(len) | ||
| { | ||
| assert(s_); | ||
| } | ||
|
|
||
| explicit CString(const char* s) : s_(s), len_(std::strlen(s_)) | ||
| { | ||
| assert(s_); | ||
| } | ||
|
|
||
| constexpr const char* c_str() const | ||
| { | ||
| return s_; | ||
| } | ||
| /// Find the first occurrence of the character and return a pointer to it or NULL if not found | ||
| const char* find(char c) const | ||
| { | ||
| return static_cast<const char*>(std::memchr(s_, c, len_)); | ||
| } | ||
| template <size_t N> | ||
| bool starts_with(const char (&prefix)[N]) const | ||
| { | ||
| return (len_ >= N - 1u) && (std::memcmp(s_, prefix, N - 1u) == 0); | ||
| } | ||
|
|
||
| friend bool operator==(const CString& lhs, const CString& rhs) | ||
| { | ||
| return (lhs.len_ == rhs.len_) && (std::memcmp(lhs.s_, rhs.s_, rhs.len_) == 0); | ||
| } | ||
| friend bool operator!=(const CString& lhs, const CString& rhs) | ||
| { | ||
| return !(lhs == rhs); | ||
| } | ||
| }; | ||
|
|
||
| } // namespace scorepy |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,49 +37,60 @@ static const std::array<std::string, 2> EXIT_REGION_WHITELIST = { | |
| #endif | ||
| }; | ||
|
|
||
| static region_handle create_user_region(const std::string& region_name, const CString module, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because this is what you have when calling the function. I could add an overloaded constructor to |
||
| const CString file_name, const std::uint64_t line_number) | ||
| { | ||
| region_handle handle; | ||
| SCOREP_User_RegionInit(&handle.value, NULL, NULL, region_name.c_str(), | ||
| SCOREP_USER_REGION_TYPE_FUNCTION, file_name.c_str(), line_number); | ||
|
|
||
| // Extract main module name if module is like "mainmodule.submodule.subsubmodule" | ||
| const char* dot_pos = module.find('.'); | ||
| if (dot_pos) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to move this to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain what you want to move exactly? Keep in mind that allocations should be avoided hence a string copy must only be done when required, in this case when a dot was found |
||
| { | ||
| const std::string main_module(module.c_str(), dot_pos); | ||
| SCOREP_User_RegionSetGroup(handle.value, main_module.c_str()); | ||
| } | ||
| else | ||
| { | ||
| SCOREP_User_RegionSetGroup(handle.value, module.c_str()); | ||
| } | ||
| return handle; | ||
| } | ||
|
|
||
| // Used for regions, that have an identifier, aka a code object id. (instrumenter regions and | ||
| // some decorated regions) | ||
| void region_begin(const std::string& function_name, const std::string& module, | ||
| const std::string& file_name, const std::uint64_t line_number, | ||
| const std::uintptr_t& identifier) | ||
| void region_begin(const CString function_name, const CString module, const CString file_name, | ||
| const std::uint64_t line_number, const std::uintptr_t& identifier) | ||
| { | ||
| auto& region_handle = regions[identifier]; | ||
|
|
||
| if (region_handle == uninitialised_region_handle) | ||
| { | ||
| auto& region_name = make_region_name(module, function_name); | ||
| SCOREP_User_RegionInit(®ion_handle.value, NULL, NULL, region_name.c_str(), | ||
| SCOREP_USER_REGION_TYPE_FUNCTION, file_name.c_str(), line_number); | ||
|
|
||
| SCOREP_User_RegionSetGroup(region_handle.value, | ||
| std::string(module, 0, module.find('.')).c_str()); | ||
| const auto& region_name = make_region_name(module, function_name); | ||
| region_handle = create_user_region(region_name, module, file_name, line_number); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please either rename to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The point is that we duplicate the code on how a user region is created and that code is slightly more than the 2 And why |
||
| } | ||
| SCOREP_User_RegionEnter(region_handle.value); | ||
| } | ||
|
|
||
| // Used for regions, that only have a function name, a module, a file and a line number (user | ||
| // regions) | ||
| void region_begin(const std::string& function_name, const std::string& module, | ||
| const std::string& file_name, const std::uint64_t line_number) | ||
| void region_begin(const CString function_name, const CString module, const CString file_name, | ||
| const std::uint64_t line_number) | ||
| { | ||
| const auto& region_name = make_region_name(module, function_name); | ||
| auto& region_handle = user_regions[region_name]; | ||
|
|
||
| if (region_handle == uninitialised_region_handle) | ||
| { | ||
| SCOREP_User_RegionInit(®ion_handle.value, NULL, NULL, region_name.c_str(), | ||
| SCOREP_USER_REGION_TYPE_FUNCTION, file_name.c_str(), line_number); | ||
|
|
||
| SCOREP_User_RegionSetGroup(region_handle.value, | ||
| std::string(module, 0, module.find('.')).c_str()); | ||
| region_handle = create_user_region(region_name, module, file_name, line_number); | ||
| } | ||
| SCOREP_User_RegionEnter(region_handle.value); | ||
| } | ||
|
|
||
| // Used for regions, that have an identifier, aka a code object id. (instrumenter regions and | ||
| // some decorated regions) | ||
| void region_end(const std::string& function_name, const std::string& module, | ||
| const std::uintptr_t& identifier) | ||
| void region_end(const CString function_name, const CString module, const std::uintptr_t& identifier) | ||
| { | ||
| const auto it_region = regions.find(identifier); | ||
| if (it_region != regions.end()) | ||
|
|
@@ -94,7 +105,7 @@ void region_end(const std::string& function_name, const std::string& module, | |
| } | ||
|
|
||
| // Used for regions, that only have a function name, a module (user regions) | ||
| void region_end(const std::string& function_name, const std::string& module) | ||
| void region_end(const CString function_name, const CString module) | ||
| { | ||
| auto& region_name = make_region_name(module, function_name); | ||
| const auto it_region = user_regions.find(region_name); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,30 +1,29 @@ | ||
| #pragma once | ||
|
|
||
| #include "cstring.h" | ||
| #include <cstdint> | ||
| #include <string> | ||
|
|
||
| namespace scorepy | ||
| { | ||
| /// Combine the arguments into a region name | ||
| /// Return value is a statically allocated string to avoid memory (re)allocations | ||
| inline const std::string& make_region_name(const std::string& module_name, const std::string& name) | ||
| inline const std::string& make_region_name(const CString module_name, const CString name) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As written I would vote for implementing this returning
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The result is used to search a map with keys of |
||
| { | ||
| static std::string region; | ||
| region = module_name; | ||
| region = module_name.c_str(); | ||
| region += ":"; | ||
| region += name; | ||
| region += name.c_str(); | ||
| return region; | ||
| } | ||
|
|
||
| void region_begin(const std::string& function_name, const std::string& module, | ||
| const std::string& file_name, const std::uint64_t line_number, | ||
| const std::uintptr_t& identifier); | ||
| void region_begin(const std::string& function_name, const std::string& module, | ||
| const std::string& file_name, const std::uint64_t line_number); | ||
| void region_begin(CString function_name, CString module, CString file_name, | ||
| const std::uint64_t line_number, const std::uintptr_t& identifier); | ||
| void region_begin(CString function_name, CString module, CString file_name, | ||
| const std::uint64_t line_number); | ||
|
|
||
| void region_end(const std::string& function_name, const std::string& module, | ||
| const std::uintptr_t& identifier); | ||
| void region_end(const std::string& function_name, const std::string& module); | ||
| void region_end(CString function_name, CString module, const std::uintptr_t& identifier); | ||
| void region_end(CString function_name, CString module); | ||
|
|
||
| void region_end_error_handling(const std::string& region_name); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,8 @@ | ||
| #pragma once | ||
|
|
||
| #include "cstring.h" | ||
| #include <Python.h> | ||
| #include <cassert> | ||
| #include <frameobject.h> | ||
| #include <string> | ||
| #include <type_traits> | ||
|
|
@@ -71,11 +73,37 @@ auto cast_to_PyFunc(TFunc* func) -> detail::ReplaceArgsToPyObject_t<TFunc>* | |
| return reinterpret_cast<detail::ReplaceArgsToPyObject_t<TFunc>*>(func); | ||
| } | ||
|
|
||
| inline CString get_string_from_python(PyObject& o) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add this as function, or initialiser of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can do that. However the intention of What is wrong with this function? |
||
| { | ||
| #if PY_MAJOR_VERSION >= 3 | ||
| Py_ssize_t len; | ||
| const char* s = PyUnicode_AsUTF8AndSize(&o, &len); | ||
| return CString(s, len); | ||
| #else | ||
| const char* s = PyString_AsString(&o); | ||
| return CString(s); | ||
| #endif | ||
| } | ||
|
|
||
| /// Pair of a C-String and it's length useful for PyArg_ParseTuple with 's#' | ||
| /// Implicitely converts to CString. | ||
| struct PythonCString | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not merging
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| { | ||
| const char* s; | ||
| Py_ssize_t l; | ||
| operator CString() const | ||
| { | ||
| assert(s); | ||
| return CString(s, l); | ||
| } | ||
| }; | ||
|
|
||
| /// Return the module name the frame belongs to. | ||
| /// The pointer is valid for the lifetime of the frame | ||
| const char* get_module_name(const PyFrameObject& frame); | ||
| CString get_module_name(const PyFrameObject& frame); | ||
| /// Return the file name the frame belongs to | ||
| std::string get_file_name(const PyFrameObject& frame); | ||
| /// The returned CString is valid until the next call to this function | ||
| CString get_file_name(const PyFrameObject& frame); | ||
|
|
||
| // Implementation details | ||
| namespace detail | ||
|
|
||
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.
What about an Initalisert, which takes the frame?
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.
Can you elaborate?