- 2024-11-02 oatpp/oatpp: 🌱Light and powerful C++ web framework for highly scalable and resource-efficient web application. It's zero-dependency and easy-portable. { github.com } http server / framework
- 2024-11-02 Example ApiClient | Oat++ { oatpp.io }
Oat++ is an open-source C++ web framework for highly scalable and resource-efficient web applications. It provides all the necessary components for production-grade development, including:
- Advanced REST framework with request parameters mapping and Swagger-UI annotations. See Api Controller and Api Client.
- ORM Framework. See Object-Relational Mapping (ORM)
- WebSocket framework. See 5-million connections benchmark.
- Object Mapping. See Data Transfer Object (DTO).
- Dependency Injection
- Swagger-UI. See oatpp-swagger module.
It's zero-dependency, easy-portable, and high-performance
Despite the number of changes in this pull request and the many files modified, most of this is due to the introduction of a new styling format with clang-format
. I reformatted a lot of files, fixed some bugs (which I don't fully recall), and made a major change by working to migrate and extract library functions from awwlib
into a separate repository also named awwlib
.
The plan moving forward is to integrate Lua as the language for configuration and scripting, with support for aww-run
. To proceed with these next steps, I need to merge these changes now and start with a clean slate.
I wanted to test if I can apply "Curiously recurring template pattern (CRTP)" instead of using virtual polymorphism.
CRTP offers compile time polymorphism, which, I suppose can be faster, but it requires some hacks.
And the main hack, is that instead of this in the interface declaration:
int aww_date_main(const std::vector<std::string>& cmdArgs,
aww_date_io_dependencies_interface& deps);
}
I would need to do this:
int aww_date_main(const std::vector<std::string>& cmdArgs,
auto& deps);
}
which makes the interface ambiguous for the reader. I really want to declare that the tool needs aww_date_io_dependencies_interface&
.
Read more:
- c++ - What is the curiously recurring template pattern (CRTP)? - Stack Overflow { stackoverflow.com }
- 2024-10-27 4. Polymorphism and CRTP { blog.zharii.com }
Update: Oh, I found this, so the problematic auto
code can be expressed as:
template<typename T>
int aww_date_main(const std::vector<std::string>& cmdArgs,
aww_date_io_dependencies_interface<T>& deps);
}
I might return back to this note and reconsider CRTP
2024-10-28 📌 The Curiously Recurring Template Pattern (CRTP) - Fluent C++ { www.fluentcpp.com }
- The CRTP, episode One: Definition
- The CRTP, episode Two: What the CRTP can bring to your code
- The CRTP, episode Three: An implementation helper for the CRTP
Here is the converted CRTP code without fix:
#pragma once
#ifndef AWW_DATE_HPP
#define AWW_DATE_HPP
#include <string>
#include <vector>
#include "aww-common.hpp"
#include "clip.h"
namespace aww::internal::aww_date {
template <typename Derived> class aww_date_io_dependencies_interface {
public:
~aww_date_io_dependencies_interface() = default;
std::string get_date_yyyymmdd([[maybe_unused]] aww::call_tag_t tag) {
return static_cast<const Derived*>(this)->get_date_yyyymmdd(tag);
}
bool clipboard_set_text(const std::string& text, [[maybe_unused]] aww::call_tag_t tag) {
return static_cast<const Derived*>(this)->clipboard_set_text(text, tag);
}
void show_notification(const std::string& title, const std::string& message,
[[maybe_unused]] aww::call_tag_t tag) {
static_cast<const Derived*>(this)->show_notification(title, message, tag);
}
};
class aww_date_io_dependencies
: public aww_date_io_dependencies_interface<aww_date_io_dependencies> {
public:
std::string get_date_yyyymmdd([[maybe_unused]] aww::call_tag_t tag) {
return aww::date::get_date_YYYYMMDD();
}
bool clipboard_set_text(const std::string& text, [[maybe_unused]] aww::call_tag_t tag) {
return clip::set_text(text);
}
void show_notification(const std::string& title, const std::string& message,
[[maybe_unused]] aww::call_tag_t tag) {
aww::os::actions::show_notification(title, message);
}
};
int aww_date_main(const std::vector<std::string>& cmdArgs,
aww_date_io_dependencies_interface& deps);
} // namespace aww::internal::aww_date
#endif
Revert!
clang-format 'em all!
Get-ChildItem -Recurse -Include *.cpp, *.h, *.c, *.hpp | Where-Object { $_.FullName -notlike '*\third-party\*' } | ForEach-Object { clang-format.exe -i $_.FullName }
Started lib experimental migration to [dzharii/awwlib-cpp: aww project internal] awwlib is cross-platform C++ utility library that provides commonly used helper functions for handling environment variables, string manipulations, and date/time conversions.
Migrated:
Returned webview!
Working on integration with:
2024-03-10 libcpr/cpr: C++ Requests: Curl for People, a spiritual port of Python Requests.
With some compilation issues / dll sharing issues.
Code cleanup:
I've removed the webview/webview dependency, a tiny cross-platform webview library for C/C++ using WebKit and Edge WebView2. While it's an awesome tool for building cross-platform browser GUIs, I couldn't find a suitable application for it in the awwtools project. It might be better suited for a standalone project.
Tiny update to CXX 20. No issues so far.
My most used and most simple aww tool aww-date
got some experimental update.
I have found that tracking the input/output dependencies calls is tricky, since, for instance, fs_file_exists()
can be called multiple times in the code for different cases. In the test cases, I want to somehow recreate the complex code path, but
tracking the right calls by their call order (number of times same function was called) or by the input parameters is too brittle, how can I precisely find the call I need? at first, for a long time, my idea was to rename fs_file_exists()
to a case specific name, like:
- if
fs_template_folder_exists()
- if
fs_template_file_exists()
- do something
- else if
fs_template_file_with_specification_exists()
- do something else
- if
but to support this case I have to:
- add these function to
aww_create_io_dependencies_interface
- then implement in
aww_create_io_dependencies
- then create a mock in
aww_create_io_dependencies_stub
This simple does not scale, so I have decided to tag these calls at compile time and keep the generic name.
New code added:
include\aww-common.hpp
// CallTag struct definition
struct call_tag_t {
constexpr explicit call_tag_t(unsigned long long value) : value(value) {}
const unsigned long long value;
};
// Compile-time hash function
constexpr unsigned long long _compiletime_hash(const char* str, unsigned long long hash = 0, size_t index = 0) {
return str[index] ? _compiletime_hash(str, (hash * 131) + str[index], index + 1) : hash;
}
// call_tag function with compile-time length check for string literals
template <size_t N>
constexpr call_tag_t call_tag(const char (&str)[N]) {
static_assert(N > 11, "Tag string must be at least 11 characters long."); // N includes the null terminator
return call_tag_t(_compiletime_hash(str));
}
Usage at src\internal\aww-date.cpp
if (deps.clipboard_set_text(result, aww::call_tag("t7svmrrhai0"))) {
std::cout << "Copied to clipboard: " << result << "\n";
deps.show_notification("aww date", "The date has been copied to the clipboard", aww::call_tag("tssis4p5ta2"));
} else {
std::cout << "Failed to copy to clipboard: " << result << "\n";
deps.show_notification("aww date", "Failed to copy the date to the clipboard", aww::call_tag("730v5jc2d3o"));
return 1;
}
Added first dev sketch:
aww structured logging ./doc-dev-articles\2023-12-02-aww-tools-structured-logging.md
aww-open
refactored with io dependency and unit tests
aww run mdtree -FilterName ".*aww-open.*" -Output dev-experiments-unit-tests\output-aww-open.md
My current unit-testing strategy feels overwhelming:
Dependency:
- Interface, implementation
- Test implementation, manual stubs
It requires some time and discipline, but it seems to be working.
For instance, now I can write unit-tests or experiment by replacing parts of the
tool with something new and keep the previous implementation for other tools.
The Dependency patterns gives me this flexibility.
Today, I have hopefully replaces all dependencies in aww-git-open
I have designed an new prompt to assist me with stub generation for unit-tests, here it is:
When I say a command !implement: followed by function signature, I want you to write the implementation for the C++ function. Do not put any extra boilerplate code (like class definitions), just give me the implementation close to the example below. Example, my request:
!implement:
virtual inline aww::Result launch_file_in_browser(const std::string &url) = 0;
Your response:
file io_dependencies_interface
:
/**
* Launch a file in the default browser.
* @param url The url to launch.
* @return Aww::Result indicating the success or failure of the operation.
*/
virtual inline aww::Result launch_file_in_browser(const std::string &url) = 0;
file io_dependencies
:
inline aww::Result launch_file_in_browser(const std::string &url) override
{
// TODO: implement me
}
file io_dependencies_stub
:
// always define a default stub function (do not put this comment in the resulting code)
std::function<aww::Result(const std::string&)>
launch_file_in_browser_stub = [this]([[maybe_unused]]const std::string &url) -> aww::Result
{
return aww::Result::ok();
};
// always define a counter (do not put this comment in the resulting code)
int launch_file_in_browser_called = 0;
// always call the stub function and increment the call counter (do not put this comment in the resulting code)
aww::Result launch_file_in_browser(const std::string &url) override
{
launch_file_in_browser_called += 1;
return launch_file_in_browser_stub(url);
}
Also dot-awwtools/aww-scripts/mdtree.ps1 at main · dzharii/dot-awwtools · GitHub helps a lot for setting GPT context and asking it to generate tests.
aww run mdtree -FilterName ".*aww-guid.*"
I want you to be an expert developer in Test in modern C++ language who:
- writes solid and simple-to-understand unit-test for C++ projects
- uses doctest/doctest.h library for unit-testing
- prioritizes the most important tests to cover the most important code first
- writes bug-free and elegant code.
In my next message, I will give you a task; before providing your answer with the code, brainstorm it internally:
- find and fix possible bugs
- make code organization more elegant
- ensure the code is testable and advice how to improve testability
- ensure there are no security vulnerabilities.
Codding style:
- for unused parameters use
[[maybe_unused]]
attribute
Attempt to fix the issues from discovered in the brainstorm and only then provide the answer to my request. Say "Ack" to acknowledge or ask me any questions that may improve your response. And then wait for my next request with the task for you.
- Tiny refactoring for aww-git-open. Ready for making unit-tests.
The error handling aww::Result code for I/O operations is too verbose and complex. I've decided to revert some code to use std::filesystem functions instead and added global error handler.
int main(int argc, char **argv)
{
try
{
std::vector<std::string> cmdArgs(argv, argv + argc);
cmdArgs.erase(cmdArgs.begin()); // remove first element
aww::internal::aww_create::aww_create_io_dependencies deps;
return aww::internal::aww_create::aww_create_main(cmdArgs, deps);
}
catch (std::exception &ex)
{
std::cerr << ex.what() << "\n";
return 1;
}
catch (...)
{
std::cerr << "Caught unknown exception.\n";
return 1;
}
The exception situation is truly should be !! exceptional !!, so I'd rather handle I/O error in the global handler.
The idea about returning aw::Result from each I/O operation adds lots of ugly error handling code.
nope, maybe some global try/cath will be better. Maybe I should whitch back to std::* io opearions.
fs::path currentDir;
if (aww::Result res = deps.fs_get_current_directory_absolute_path(currentDir); res.is_failed())
{
std::cout << "Failed to get current directory"
<< res.error()
<< "\n";
return 1;
}
aww::Result improving experiment
2023-06-20 if statement - cppreference.com
if (aww::Result res = deps.fs_get_current_directory_absolute_path(currentDir); res.is_failed())
{
std::cout << "Failed to get current directory"
<< res.error()
<< "\n";
return 1;
}
aww_git_open
started refactoring to extract I/O dependencies
- Refactor / move from
aww create
toawwlib
aww::fs::
file_or_dir_exists, create_directories,- Rename camel case to snake case
- Use
aww-run-aww
for custom cmd, sh and ps1 scripts
- The Great Unit testing refactoring
- Introduced dependency pattern for input/output dependencies.
- refactored
aww date
andaww create
to use dependency injection and covered the utils with unit-tests. CPP_HEADER_FILE_NAME
renamed toCAPITALIZED_FILE_NAME
This is important milestone to make code more stable and perform less manual adhoc tests.
- fixed bug in aww run when failed command was reported as failed and then successful. O_O
- wintoastlib -- removed debug output
aww.cpp
now provides a static definition of the tool list and passes arguments directly to the tool's main()
function using const std::vector<std::string> &cmdArgs
. This change eliminates the need to check for the existence of aww-tool.exe
using the filesystem and replaces the previous flow of reading arguments, serializing them as a string, and passing them to the system()
call. Now, arguments are passed directly to the tool's main function as shown in the example below:
Before:
aww tool-name => read args => serialize args back as string => find-if aww-tool exists => pass args as a string via system(...) call.
After:
aww date one two three => aww::internal::aww_date::aww_date_main(awwExecutableArgs)
These changes result in a more efficient and streamlined workflow for using the aww tools.
- add more
aww create
templates for powershell cmd and sh companions
- fixed bug in
aww create
:CPP_HEADER_FILE_NAME
to generate valid identifiers.
the commands:
aww create .\include\internal\aww-xyz.hpp
aww create .\src\internal\aww-xyz.cpp
were very helpful during migration to lib.
Migrated to library:
aww-create.cpp
aww-git-open.cpp
aww-guid.cpp
- ... and all others
Changelog created!
aww create
now creates folders when the file name does not have an extension. This makes it impossible to create extension-less files, but for the sake of simplicity -- it is what it is.
aww date
-- converted to use main
implementation from lib.