-
-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
replace literally the entire lua engine
- Loading branch information
Showing
14 changed files
with
2,097 additions
and
133 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
#pragma once | ||
|
||
#include <fmt/core.h> | ||
#include <optional> | ||
#include <string> | ||
|
||
/// The Error class represents an error or the absence of an | ||
/// error. It behaves like a bool, depending on context. | ||
/// | ||
/// The idea is to use this class to pass around errors, together with | ||
/// [[nodiscard]], in order to make errors displayable for the users and | ||
/// to give errors some context. The only way to construct an error is to come | ||
/// up with an error message with this class, so this is an attempt to enforce | ||
/// this. | ||
/// | ||
/// A default constructed Error means "no error" / "success", while | ||
/// the only available non-default constructor is one which takes any format. | ||
/// For example: | ||
/// | ||
/// \code{.cpp} | ||
/// Error myfunc() { | ||
/// if (ok) { | ||
/// return {}; // no error | ||
/// } else { | ||
/// return Error("Something went wrong: {}", 42); // error | ||
/// } | ||
/// } | ||
/// | ||
/// // ... handling: | ||
/// | ||
/// Error err = myfunc(); | ||
/// if (err) { | ||
/// // error occurred | ||
/// l::error("Error running myfunc: {}", err.error); | ||
/// } else { | ||
/// // ok | ||
/// } | ||
/// \endcode | ||
struct Error { | ||
/// Constructs a "non-error" / empty error, which is not considered | ||
/// to be an error. Use this as the "no error occurred" return value. | ||
Error() = default; | ||
/// Constructs an error with a message. Accepts fmt::format() arguments. | ||
/// | ||
/// Example: | ||
/// | ||
/// \code{.cpp} | ||
/// // format with fmt (automatically), all arguments are forwarded to fmt::format | ||
/// return Error("failed to open '{}': {}", file, error); | ||
/// // or just as a constexpr string | ||
/// return Error("failed to open file"); | ||
/// \endcode | ||
template<typename... Args> | ||
Error(fmt::format_string<Args...> s, Args&&... args) | ||
: is_error(true) | ||
, error(fmt::format(s, std::forward<Args>(args)...)) { } | ||
|
||
/// Whether this error represents an error (true) or success (false). | ||
/// Use operator bool() instead of reading this if possible. | ||
bool is_error { false }; | ||
/// The error message. Is a valid string even if is_error is false, but will | ||
/// be "Success". | ||
std::string error { "Success" }; | ||
|
||
/// Implicit conversion to boolean. | ||
/// True if this Error contains an error, false if not. | ||
operator bool() const { return is_error; } | ||
}; | ||
|
||
// TODO: Add docs | ||
|
||
template<typename T> | ||
struct Result { | ||
/// Constructs an error-value result. | ||
/// Currently, you may have to force this by passing a second | ||
/// empty string argument. | ||
template<typename... Args> | ||
Result(fmt::format_string<Args...> s, Args&&... args) | ||
: is_error(true) | ||
, error(fmt::format(s, std::forward<Args>(args)...)) { } | ||
|
||
/// Constructs a value-result via an explicit type. | ||
template<typename S> | ||
Result(S&& value) | ||
: result(std::move(value)) { | ||
} | ||
|
||
/// Constructs a value-result via an explicit type. | ||
template<typename S> | ||
explicit Result(const S& value) | ||
: result(value) { | ||
} | ||
|
||
/// Constructs a value-result via an implicit conversion. | ||
Result(T&& value) | ||
: result(std::move(value)) { | ||
} | ||
|
||
/// Constructs a value-result via an implicit conversion. | ||
explicit Result(const T& value) | ||
: result(value) { | ||
} | ||
|
||
/// Converts to bool in context. If it has an error, its considered "not a result", | ||
/// so it returns true only if a value is contained. | ||
operator bool() const { return !is_error; } | ||
|
||
/// Accesses the value contained by moving it out. | ||
T&& move() { return std::move(result.value()); } | ||
/// Accesses the value contained by const reference. | ||
const T& value() const { return result.value(); } | ||
|
||
/// Holds the optional result value. On error, is nullopt. | ||
std::optional<T> result; | ||
/// Whether this result holds an error. | ||
bool is_error { false }; | ||
/// Error message. | ||
std::string error { "Success" }; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
#pragma once | ||
|
||
/// @file | ||
/// This file holds the FileWatcher interface. | ||
|
||
#include <boost/asio/deadline_timer.hpp> | ||
#include <boost/asio/io_context.hpp> | ||
#include <boost/date_time/posix_time/posix_time_duration.hpp> | ||
#include <boost/system/detail/error_code.hpp> | ||
#include <boost/thread/scoped_thread.hpp> | ||
#include <boost/thread/synchronized_value.hpp> | ||
#include <filesystem> | ||
#include <functional> | ||
#include <unordered_map> | ||
#include <unordered_set> | ||
|
||
/// The FileWatcher class watches a directory or a file for changes, | ||
/// and then notifies the caller through a signal. | ||
/// | ||
/// This is a pretty convoluted implementation, and you may find it difficult | ||
/// to read. This is not intentional, but simplifying this would | ||
/// cost more time than to write this excuse. | ||
/// | ||
/// It operates as follows: | ||
/// | ||
/// A boost::asio::deadline_timer is waited on asynchronously. | ||
/// Once expired, this timer calls FileWatcher::on_tick. | ||
/// That function then loops through all registered files and directories, | ||
/// taking great care to follow symlinks, and tries to find a file which has changed. | ||
/// It determines this by storing the last known modification time. | ||
/// Once a file is found which has a new modification time, the FileWatcher::sig_file_changed | ||
/// signal is fired, and all connected slots must take care to handle the signal. | ||
class FileWatcher { | ||
public: | ||
/// Constructs the FileWatcher to watch the given files every few seconds, as | ||
/// specified by the seconds argument. | ||
FileWatcher(unsigned seconds); | ||
/// Stops the thread via m_shutdown. | ||
~FileWatcher(); | ||
|
||
/// Add a file to watch. If this file changes, FileWatcher::sig_file_changed is triggered | ||
/// with the path to the file. | ||
void watch_file(const std::filesystem::path& path); | ||
/// Add a directory to watch. If any file in the directory, or any subdirectories, change, | ||
/// FileWatcher::sig_file_changed is triggered. | ||
void watch_files_in(const std::filesystem::path& dir); | ||
|
||
private: | ||
/// Entry point for the timer thread. | ||
void thread_main(); | ||
|
||
/// Called every time the timer runs out, watches for file changes, then starts | ||
/// a new timer. | ||
void on_tick(const boost::system::error_code&); | ||
|
||
/// Checks files for changes, calls FileWatcher::sig_file_changed on change. | ||
void check_files(); | ||
/// Checks directories for files which changed, calls FileWatcher::sig_file_changed on change. | ||
void check_directories(); | ||
/// Checks a single file for change. | ||
void check_file(const std::filesystem::path& file); | ||
|
||
/// Interval in seconds for the timer. Needed to be able to restart the timer over and over. | ||
boost::synchronized_value<boost::posix_time::seconds> m_seconds; | ||
/// If set, the thread handling the file watching will shut down. Set in the destructor. | ||
boost::synchronized_value<bool> m_shutdown { false }; | ||
/// Io context handles the scheduling of timers on the thread. | ||
boost::asio::io_context m_io {}; | ||
/// Holds all files that are to be checked. | ||
/// | ||
/// It uses a boost::hash<> because in the original C++17 | ||
/// standard, std::hash of a filesystem path was not defined, and as such | ||
/// some implementations still don't have it. | ||
/// See https://cplusplus.github.io/LWG/issue3657 | ||
boost::synchronized_value<std::unordered_set<std::filesystem::path, boost::hash<std::filesystem::path>>> m_files {}; | ||
/// Holds all the directories that are to be searched for files to be checked. | ||
/// | ||
/// See FileWatcher::m_files for an explanation for the boost::hash. | ||
boost::synchronized_value<std::unordered_set<std::filesystem::path, boost::hash<std::filesystem::path>>> m_dirs {}; | ||
/// Holds the last known modification times of all found files. | ||
std::unordered_map<std::filesystem::path, std::filesystem::file_time_type, boost::hash<std::filesystem::path>> m_file_mod_times {}; | ||
/// Timer used to time the checks. Restarted every FileWatcher::m_seconds seconds. | ||
boost::synchronized_value<boost::asio::deadline_timer> m_timer; | ||
/// Work guard helps the io_context "sleep" while there is no work to be done - must be reset in the | ||
/// destructor in order to not cause work to be thrown away (though in this case we probably don't care). | ||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_work_guard = boost::asio::make_work_guard(m_io); | ||
/// Thread on which all watching and timing work runs. | ||
boost::scoped_thread<> m_thread; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#pragma once | ||
|
||
/// @file | ||
/// HashMap holds hash map implementations and typedefs. | ||
/// | ||
/// The idea is that we can easily swap out the implementation | ||
/// in case there is a performance or memory usage concern. | ||
|
||
#include <boost/container/flat_map.hpp> | ||
#include <boost/thread/synchronized_value.hpp> | ||
|
||
/// A hash map to be used for any kind of small number of key-value pairs. | ||
/// Iterators and pointers may be invalidated on modification. | ||
template<typename K, typename V> | ||
using HashMap = boost::container::flat_map<K, V>; | ||
|
||
/// A synchronized hash map is a hash map in which each | ||
/// access is thread-safe. In this case, this is achieved by locking | ||
/// each access with a mutex (which often ends up being a futex in the implementation). | ||
template<typename K, typename V> | ||
using SynchronizedHashMap = boost::synchronized_value<boost::container::flat_map<K, V>>; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.