Compare commits
15 Commits
31c624797f
...
master
Author | SHA1 | Date | |
---|---|---|---|
ab8489c377 | |||
c48e79753d | |||
eda801d3c7 | |||
64045b1d48 | |||
8e0865384d | |||
c6c450f6fa | |||
3dd0c85995 | |||
5859264eca | |||
d69563b5df | |||
446f880df4 | |||
05a80268ab | |||
19d0a5bb4d | |||
e7a05b3488 | |||
82c3ed5b32 | |||
d6be8a11ac |
18
.github/linux_build.sh
vendored
Normal file
18
.github/linux_build.sh
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Create build directory and enter it
|
||||
mkdir bin
|
||||
cd bin
|
||||
# Create internal build and install directory, then enter it
|
||||
mkdir build
|
||||
mkdir install
|
||||
cd build
|
||||
|
||||
# Build in Release mode
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ../..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
|
||||
# Back to root directory
|
||||
cd ..
|
||||
cd ..
|
18
.github/windows_build.bat
vendored
Normal file
18
.github/windows_build.bat
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
@ECHO OFF
|
||||
|
||||
:: Create build directory and enter it
|
||||
MKDIR bin
|
||||
CD bin
|
||||
:: Create internal build and install directory, then enter it
|
||||
MKDIR build
|
||||
MKDIR install
|
||||
CD build
|
||||
|
||||
:: Build with x64 architecture in Release mode
|
||||
cmake -A x64 ../..
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Relese
|
||||
|
||||
:: Back to root directory
|
||||
CD ..
|
||||
CD ..
|
@ -10,7 +10,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# Provide options
|
||||
option(YYCC_BUILD_TESTBENCH "Build testbench of YYCCommonplace." OFF)
|
||||
option(YYCC_BUILD_TEST "Build test of YYCCommonplace." OFF)
|
||||
option(YYCC_BUILD_BENCHMARK "Build benchmark of YYCCommonplace." OFF)
|
||||
option(YYCC_BUILD_DOC "Build document of YYCCommonplace." OFF)
|
||||
option(YYCC_ENFORCE_ICONV "Enforce iconv support for this library (e.g. in MSYS2 environment)." OFF)
|
||||
|
||||
@ -26,21 +27,28 @@ set(YYCC_INSTALL_DOC_PATH ${CMAKE_INSTALL_DOCDIR} CACHE PATH
|
||||
"Non-arch doc install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.")
|
||||
|
||||
# Include dependency.
|
||||
# GTest is required if we build testbench
|
||||
if (YYCC_BUILD_TESTBENCH)
|
||||
# GTest is required if we build test
|
||||
if (YYCC_BUILD_TEST)
|
||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
find_package(GTest REQUIRED)
|
||||
endif ()
|
||||
# Google Benchmark is required if we build benchmark
|
||||
if (YYCC_BUILD_BENCHMARK)
|
||||
find_package(benchmark REQUIRED)
|
||||
endif ()
|
||||
# Iconv is required if we are not in Windows or user request it
|
||||
if (YYCC_ENFORCE_ICONV OR (NOT WIN32))
|
||||
find_package(Iconv REQUIRED)
|
||||
endif ()
|
||||
|
||||
# Import 3 build targets
|
||||
# Import 4 build targets
|
||||
add_subdirectory(src)
|
||||
if (YYCC_BUILD_TESTBENCH)
|
||||
add_subdirectory(testbench)
|
||||
if (YYCC_BUILD_TEST)
|
||||
add_subdirectory(test)
|
||||
endif ()
|
||||
if (YYCC_BUILD_BENCHMARK)
|
||||
add_subdirectory(benchmark)
|
||||
endif ()
|
||||
if (YYCC_BUILD_DOC)
|
||||
add_subdirectory(doc)
|
||||
|
71
COMPILE.md
71
COMPILE.md
@ -11,40 +11,65 @@ It means that it is not stable and work in progress.
|
||||
* CMake 3.23 at least.
|
||||
* The common compiler supporting C++ 23 (GCC / Clang / MSVC).
|
||||
* Iconv (Optional on Windows. Required on other systems).
|
||||
* [GoogleTest](https://github.com/google/googletest) (Required if you build testbench).
|
||||
* [Google Test](https://github.com/google/googletest) (Required if you build test).
|
||||
* [Google Benchmark](https://github.com/google/benchmark) (Required if you build benchmark).
|
||||
* Doxygen (Required if you build documentation).
|
||||
* Python and Astral UV (Required if you use "User Build" method)
|
||||
|
||||
If you are just want to build this project to make something works, or build other project, rather than code with it,
|
||||
you commonly do not need build test, benchmark and documentation.
|
||||
So you actually do not need Google Test, Google Benchmark and Doxygen.
|
||||
|
||||
## Preparing
|
||||
|
||||
### Compiler
|
||||
|
||||
> [!WARNING]
|
||||
> You may face some issues when building on macOS with Clang. That's not your fault.
|
||||
> Clang used libc++ library lacks some essential features used by this project.
|
||||
> You may try other solutions for compiling this project on macOS or with Clang.
|
||||
> A possible solution is that use GCC and libstdc++ on macOS instead of default Clang and libc++.
|
||||
> Build issue may be resolved until libc++ finish these features: `std::stacktrace` and `std::views::enumerate`.
|
||||
|
||||
## Preparing
|
||||
### Google Test
|
||||
|
||||
### GoogleTest
|
||||
|
||||
GoogleTest is required if you need to build testbench.
|
||||
Google Test is required if you need to build test.
|
||||
If you don't need this please skip this chapter.
|
||||
|
||||
We use GoogleTest v1.17.0.
|
||||
We use Google Test v1.17.0.
|
||||
It would be okey use other versions but I have not test on them.
|
||||
|
||||
> [!WARNING]
|
||||
> When building this project, you may face link error with GoogleTest, especially on Linux.
|
||||
> When building this project, you may face link error with Google Test, especially on Linux.
|
||||
> This issue is caused by that the binary provided by your package manager is built in C++17 and its ABI is incompatible with C++23.
|
||||
> See this [GitHub Issue](https://github.com/google/googletest/issues/4591) for more infomation.
|
||||
> The solution is that download GoogleTest source code and build it in C++23 on your own.
|
||||
> The solution is that download Google Test source code and build it in C++23 on your own.
|
||||
> Following content tell you how to do this.
|
||||
|
||||
There are the steps instructing you how to compile GoogleTest manually.
|
||||
There are the steps instructing you how to compile Google Test manually.
|
||||
|
||||
1. Download GoogleTest source code with given version in GitHub Release page.
|
||||
1. Download Google Test source code with given version in GitHub Release page.
|
||||
1. Extract it into a directory.
|
||||
1. Enter this directory and create 2 subdirectory `build` and `install` for CMake build and install respectively.
|
||||
1. Enter `build` directory and configure CMake with extra `-DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON` parameters.
|
||||
1. Use CMake to build GoogleTest
|
||||
1. Use CMake to install GoogleTest into previous we created `install` directory.
|
||||
1. Use CMake to build Google Test
|
||||
1. Use CMake to install Google Test into previous we created `install` directory.
|
||||
|
||||
### Google Benchmark
|
||||
|
||||
Google Benchmark is required if you need to build benchmark.
|
||||
If you don't need this please skip this chapter.
|
||||
|
||||
We use Google Benchmark v1.9.4.
|
||||
It would be okey use other versions but I have not test on them.
|
||||
|
||||
There are the steps instructing you how to compile Google Benchmark manually.
|
||||
|
||||
1. Download Google Benchmark source code with given version in GitHub Release page.
|
||||
1. Extract it into a directory.
|
||||
1. Enter this directory and create link named `googletest` to previous fetched Google Test root directory. This is instructed by official manual because Google Benchmark rely on Google Test. Link can be create by executing `mklink /D googletest <path-to-googletest-root-dir>` on Windows or `ln -s <path-to-googletest-root-dir> googletest` on POSIX-like OS.
|
||||
1. Keep stay in this directory and create 2 subdirectory `build` and `install` for CMake build and install respectively.
|
||||
1. Enter `build` directory and configure CMake with extra `-DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF` parameters.
|
||||
1. Use CMake to build Google Benchmark
|
||||
1. Use CMake to install Google Benchmark into previous we created `install` directory.
|
||||
|
||||
### Iconv
|
||||
|
||||
@ -71,33 +96,33 @@ So before compiling, you must make sure `doxygen` are presented in your environm
|
||||
## Build and Install
|
||||
|
||||
There are 2 different ways to build this project.
|
||||
If you are the user of this project (just want this project to make something work), please choose "User Build".
|
||||
If you are the user of this project (just want this project to make something works, or build other projects), please choose "User Build".
|
||||
If you are a developer (developer of this project, or use this project as dependency to develop your project), please choose "Developer Build".
|
||||
|
||||
### User Build
|
||||
|
||||
"User Build" is basically how GitHub Action build this project.
|
||||
|
||||
We use Python 3.11 and UV 0.7.17 to manage our build script generator.
|
||||
It would be okey use other versions but I have not test on them.
|
||||
|
||||
TODO...
|
||||
Execute `.github/windows_build.bat` on Windows or `.github/linux_build.sh` on POSIX-like OS (Linux and macOS) under **the root directory** of this project. The final built artifact is under `bin/install` directory.
|
||||
|
||||
### Developer Build
|
||||
|
||||
TODO...
|
||||
First, there is a list listing all variables you may configure during compiling.
|
||||
|
||||
There is a list listing all variables you may configure during compiling.
|
||||
|
||||
* `YYCC_BUILD_TESTBENCH`: Set it to `ON` to build testbench. `OFF` in default.
|
||||
* `YYCC_BUILD_TEST`: Set it to `ON` to build test. `OFF` in default.
|
||||
It is useful for the developer of this project.
|
||||
It also suit for the user who has runtime issues on their platforms to check whether this project works as expected.
|
||||
If you are debugging this project to find bug, I suggest that you build this project under Debug mode and use this test project for debugging.
|
||||
* `YYCC_BUILD_BENCHMARK`: Set it to `ON` to build benchmark. `OFF` in default.
|
||||
It is useful for the developer of this project to checking the performace for those homemade functions.
|
||||
It is highly suggested build this project with Release mode to have real benchmark result.
|
||||
* `YYCC_BUILD_DOC`: Set it to `ON` to build documentation. `OFF` in default.
|
||||
It may be useful for the developer who firstly use this project in their own projects.
|
||||
Please note that generated documentation is different in different platforms.
|
||||
* `YYCC_ENFORCE_ICONV`: Set it to `ON` to enable Iconv feature forcely. `OFF` in default.
|
||||
The usage of this option has been introduced in previous "Iconv" chapter.
|
||||
* `GTest_ROOT`: TODO
|
||||
* `benchmark_ROOT`: TODO
|
||||
* `Iconv_ROOT`: TODO
|
||||
* `CMAKE_CXX_STANDARD`: Set C++ standard version of project.
|
||||
`23` in default and this version can not be lower than C++23.
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024-2024 yyc12345
|
||||
Copyright (c) 2024-2025 yyc12345
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
28
benchmark/CMakeLists.txt
Normal file
28
benchmark/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
# Create executable benchmark
|
||||
add_executable(YYCCBenchmark "")
|
||||
# Setup test sources
|
||||
target_sources(YYCCBenchmark
|
||||
PRIVATE
|
||||
main.cpp
|
||||
|
||||
yycc/string/op.cpp
|
||||
|
||||
yycc/carton/fft.cpp
|
||||
)
|
||||
# target_sources(YYCCBenchmark
|
||||
# PRIVATE
|
||||
# FILE_SET HEADERS
|
||||
# FILES
|
||||
# shared/literals.hpp
|
||||
# )
|
||||
# Setup headers
|
||||
target_include_directories(YYCCBenchmark
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
||||
# Setup libraries
|
||||
target_link_libraries(YYCCBenchmark
|
||||
PRIVATE
|
||||
YYCCommonplace
|
||||
benchmark::benchmark
|
||||
)
|
3
benchmark/main.cpp
Normal file
3
benchmark/main.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
BENCHMARK_MAIN();
|
40
benchmark/yycc/carton/fft.cpp
Normal file
40
benchmark/yycc/carton/fft.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/fft.hpp>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
#define FFT ::yycc::carton::fft
|
||||
|
||||
namespace yyccbench::carton::fft {
|
||||
|
||||
using TIndex = size_t;
|
||||
using TFloat = float;
|
||||
using TComplex = std::complex<TFloat>;
|
||||
template<size_t N>
|
||||
using TFft = FFT::Fft<TIndex, TFloat, N>;
|
||||
|
||||
constexpr TIndex FFT_POINTS = 1024u;
|
||||
|
||||
static void BM_FftCompute(benchmark::State& state) {
|
||||
// prepare random buffer
|
||||
constexpr TIndex RND_BUF_CNT = 8u;
|
||||
std::random_device rnd_device;
|
||||
std::default_random_engine rnd_engine(rnd_device());
|
||||
std::uniform_real_distribution<TFloat> rnd_dist(0.0f, 1.0f);
|
||||
std::vector<std::vector<TComplex>> buffer_collection(RND_BUF_CNT);
|
||||
for (auto& buf : buffer_collection) {
|
||||
buf.resize(FFT_POINTS);
|
||||
std::generate(buf.begin(), buf.end(), [&rnd_engine, &rnd_dist]() mutable -> TComplex { return TComplex(rnd_dist(rnd_engine)); });
|
||||
}
|
||||
|
||||
// prepare FFT engine
|
||||
TFft<FFT_POINTS> fft;
|
||||
// do benchmark
|
||||
for (auto _ : state) {
|
||||
fft.compute(buffer_collection[state.iterations() % RND_BUF_CNT].data());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FftCompute)->Name("FftCompute");
|
||||
|
||||
}
|
28
benchmark/yycc/string/op.cpp
Normal file
28
benchmark/yycc/string/op.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
|
||||
#define OP ::yycc::string::op
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
namespace yyccbench::string::op {
|
||||
|
||||
static void BM_StringStrip(benchmark::State& state) {
|
||||
std::u8string_view strl = u8" \thello\r\n"sv, words = u8" \t\r\n"sv;
|
||||
for (auto _ : state) {
|
||||
auto rv = OP::strip(strl, words);
|
||||
benchmark::DoNotOptimize(rv);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_StringStrip)->Name("StringStrip");
|
||||
|
||||
static void BM_StringTrim(benchmark::State& state) {
|
||||
std::u8string_view strl = u8" \thello\r\n"sv, words = u8" \t\r\n"sv;
|
||||
for (auto _ : state) {
|
||||
auto rv = OP::trim(strl, words);
|
||||
benchmark::DoNotOptimize(rv);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_StringTrim)->Name("StringTrim");
|
||||
|
||||
}
|
@ -1031,7 +1031,7 @@ EXCLUDE_SYMBOLS =
|
||||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH = @CMAKE_CURRENT_LIST_DIR@/../testbench
|
||||
EXAMPLE_PATH = @CMAKE_CURRENT_LIST_DIR@/../test
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
|
@ -21,7 +21,7 @@ public:
|
||||
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true),
|
||||
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr),
|
||||
m_ClampedFloatArgument(YYCC_U8("clamped-float"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
|
||||
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the testbench of argument parser."), {
|
||||
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the test of argument parser."), {
|
||||
&m_IntArgument, &m_FloatArgument, &m_StringArgument,
|
||||
&m_BoolArgument, &m_ClampedFloatArgument
|
||||
}) {}
|
||||
|
@ -29,7 +29,7 @@ In short words, this library use UTF8 encoding everywhere except some special ca
|
||||
\li Traditional format function in yycc::string::op.
|
||||
Traditional format function provide some overloads for ordinary string formatting.
|
||||
That's because this feature is so common to use in some cases.
|
||||
\li The message of Rust panic in yycc::rust::panic.
|
||||
\li The message of Rust panic in yycc::panic.
|
||||
Due to the limitation of \c std::format, we only can use ordinary string as its message content.
|
||||
\li The message of standard library exception.
|
||||
For the compatibility with C++ standard library exception,
|
||||
|
4
script/.gitignore
vendored
4
script/.gitignore
vendored
@ -2,10 +2,6 @@
|
||||
# Exclude VSCode
|
||||
.vscode/
|
||||
|
||||
# Exclude generated files
|
||||
win_build.bat
|
||||
linux_build.sh
|
||||
|
||||
## ===== Python =====
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
@ -1,106 +0,0 @@
|
||||
import argparse
|
||||
import typing
|
||||
import re
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
import jinja2
|
||||
|
||||
def validate_cpp_ver(ver: str) -> str:
|
||||
if re.match(r'^[0-9]+$', ver) is not None: return ver
|
||||
else: raise argparse.ArgumentTypeError('invalid version of C++ standard.')
|
||||
|
||||
def write_line(f: typing.TextIO, val: str) -> None:
|
||||
f.write(val)
|
||||
f.write('\n')
|
||||
|
||||
# Reference: https://stackoverflow.com/questions/29213106/how-to-securely-escape-command-line-arguments-for-the-cmd-exe-shell-on-windows
|
||||
def escape_for_cmd_exe(arg):
|
||||
meta_re = re.compile(r'([()%!^"<>&|])')
|
||||
return meta_re.sub('^\1', arg)
|
||||
def escape_cmd_argument(arg):
|
||||
if not arg or re.search(r'(["\s])', arg):
|
||||
arg = '"' + arg.replace('"', r'\"') + '"'
|
||||
return escape_for_cmd_exe(arg)
|
||||
def escape_sh_argument(arg):
|
||||
return shlex.quote(arg)
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ScriptSettings:
|
||||
pic: bool
|
||||
cpp_version: str
|
||||
build_doc: bool
|
||||
build_testbench: bool
|
||||
|
||||
class TemplateRender:
|
||||
loader: jinja2.BaseLoader
|
||||
environment: jinja2.Environment
|
||||
|
||||
win_template: jinja2.Template
|
||||
linux_template: jinja2.Template
|
||||
|
||||
settings: ScriptSettings
|
||||
|
||||
def __init__(self, settings: ScriptSettings) -> None:
|
||||
self.loader = jinja2.FileSystemLoader(self.__get_dir())
|
||||
self.environment = jinja2.Environment(loader=self.loader)
|
||||
|
||||
self.win_template = self.environment.get_template('win_build.bat.jinja')
|
||||
self.linux_template = self.environment.get_template('linux_build.sh.jinja')
|
||||
|
||||
self.settings = settings
|
||||
|
||||
def __get_dir(self) -> Path:
|
||||
return Path(__file__).resolve().parent
|
||||
|
||||
def __escape_path(self, val: str, is_win: bool) -> str:
|
||||
if is_win: return escape_cmd_argument(val)
|
||||
else: return escape_sh_argument(val)
|
||||
|
||||
def __render(self, template: jinja2.Template, dest_file: str, is_win: bool) -> None:
|
||||
with open(self.__get_dir() / dest_file, 'w', encoding='utf-8') as f:
|
||||
f.write(template.render(
|
||||
repo_root_dir = self.__escape_path(str(self.__get_dir().parent), is_win),
|
||||
cpp_version = self.settings.cpp_version,
|
||||
build_doc = self.settings.build_doc,
|
||||
pic = settings.pic
|
||||
))
|
||||
|
||||
def render_win_script(self) -> None:
|
||||
self.__render(self.win_template, 'win_build.bat', True)
|
||||
|
||||
def render_linux_script(self) -> None:
|
||||
self.__render(self.linux_template, 'linux_build.sh', False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# parse argument
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='YYCC Windows Build Script Generator',
|
||||
description='YYCC Windows Build Script Generator'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--cpp',
|
||||
action='store', default='17', dest='cpp', type=validate_cpp_ver,
|
||||
help='The version of C++ standard used when building.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d', '--build-doc',
|
||||
action='store_true', dest='build_doc',
|
||||
help='Build YYCC with documentation.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-p', '--pic',
|
||||
action='store_true', dest='pic',
|
||||
help='Enable Position Independent Code flag on non-Windows platform. This is crucial for compiling dynamic library using this library.'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# build settings
|
||||
settings = ScriptSettings(args.cpp, args.build_doc, args.pic)
|
||||
# build template render and render result
|
||||
render = TemplateRender(settings)
|
||||
render.render_win_script()
|
||||
render.render_linux_script()
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Navigate to project root directory
|
||||
cd {{ repo_root_dir }}
|
||||
|
||||
# Create build and install directory
|
||||
mkdir build
|
||||
mkdir install
|
||||
|
||||
# Build as release version
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD={{ cpp_version }} {{ '-DCMAKE_POSITION_INDEPENDENT_CODE=True' if pic }} {{ '-DYYCC_BUILD_DOC=ON' if build_doc }} {{ '-DYYCC_BUILD_TESTBENCH=ON' if build_testbench }} ../.. --fresh
|
||||
cmake --build .
|
||||
cmake --install . --prefix ../install
|
||||
|
||||
# Exit to original path
|
||||
cd ..
|
||||
echo "YYCC Linux CMake build done"
|
@ -1,85 +0,0 @@
|
||||
@ECHO OFF
|
||||
:: Navigate to project root directory
|
||||
CD /d {{ repo_root_dir }}
|
||||
:: Create build directory and enter it
|
||||
MKDIR bin
|
||||
CD bin
|
||||
MKDIR cpp{{ cpp_version }}
|
||||
CD cpp{{ cpp_version }}
|
||||
|
||||
:: Create internal build directory
|
||||
MKDIR Win32
|
||||
MKDIR x64
|
||||
MKDIR documentation
|
||||
:: Create internal install directory
|
||||
MKDIR install
|
||||
CD install
|
||||
MKDIR Win32_Debug
|
||||
MKDIR Win32_Release
|
||||
MKDIR x64_Debug
|
||||
MKDIR x64_Release
|
||||
CD ..
|
||||
:: Create internal MSVC specific install directory
|
||||
MKDIR msvc_install
|
||||
CD msvc_install
|
||||
MKDIR bin
|
||||
MKDIR include
|
||||
MKDIR lib
|
||||
MKDIR share
|
||||
CD bin
|
||||
MKDIR Win32
|
||||
MKDIR x64
|
||||
CD ..
|
||||
CD lib
|
||||
MKDIR Win32\Debug
|
||||
MKDIR Win32\Release
|
||||
MKDIR x64\Debug
|
||||
MKDIR x64\Release
|
||||
CD ..
|
||||
CD ..
|
||||
|
||||
:: Build for Win32
|
||||
CD Win32
|
||||
cmake -A Win32 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_TESTBENCH=ON ../../..
|
||||
cmake --build . --config Debug
|
||||
cmake --install . --prefix=../install/Win32_Debug --config Debug
|
||||
cmake --build . --config RelWithDebInfo
|
||||
cmake --install . --prefix=../install/Win32_Release --config RelWithDebInfo
|
||||
CD ..
|
||||
:: Build for x64
|
||||
CD x64
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_TESTBENCH=ON ../../..
|
||||
cmake --build . --config Debug
|
||||
cmake --install . --prefix=../install/x64_Debug --config Debug
|
||||
cmake --build . --config RelWithDebInfo
|
||||
cmake --install . --prefix=../install/x64_Release --config RelWithDebInfo
|
||||
CD ..
|
||||
|
||||
{% if build_doc %}
|
||||
:: Build for documentation
|
||||
CD documentation
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_DOC=ON ../../..
|
||||
cmake --build . --config RelWithDebInfo
|
||||
cmake --build . --target YYCCDocumentation
|
||||
cmake --install . --prefix=../install/x64_Release --config RelWithDebInfo
|
||||
CD ..
|
||||
{% endif %}
|
||||
|
||||
:: Copy header files
|
||||
XCOPY install\x64_Release\include msvc_install\include\ /E /Y
|
||||
:: Copy binary files
|
||||
COPY install\Win32_Release\bin\YYCCTestbench.exe msvc_install\bin\Win32\YYCCTestbench.exe /Y
|
||||
COPY install\x64_Release\bin\YYCCTestbench.exe msvc_install\bin\x64\YYCCTestbench.exe /Y
|
||||
:: Copy library files
|
||||
COPY install\Win32_Debug\lib\YYCCommonplace.lib msvc_install\lib\Win32\Debug\YYCCommonplace.lib /Y
|
||||
COPY install\Win32_Release\lib\YYCCommonplace.lib msvc_install\lib\Win32\Release\YYCCommonplace.lib /Y
|
||||
COPY install\x64_Debug\lib\YYCCommonplace.lib msvc_install\lib\x64\Debug\YYCCommonplace.lib /Y
|
||||
COPY install\x64_Release\lib\YYCCommonplace.lib msvc_install\lib\x64\Release\YYCCommonplace.lib /Y
|
||||
{% if build_doc %}
|
||||
:: Copy documentation files
|
||||
XCOPY install\x64_Release\share msvc_install\share\ /E /Y
|
||||
{% endif %}
|
||||
|
||||
:: Leave build directory and report
|
||||
CD ..\..
|
||||
ECHO Windows CMake Build Done
|
@ -15,8 +15,8 @@ PRIVATE
|
||||
yycc/string/op.cpp
|
||||
yycc/patch/fopen.cpp
|
||||
yycc/patch/stream.cpp
|
||||
yycc/rust/panic.cpp
|
||||
yycc/rust/env.cpp
|
||||
yycc/panic.cpp
|
||||
yycc/env.cpp
|
||||
yycc/windows/com.cpp
|
||||
yycc/windows/dialog.cpp
|
||||
yycc/windows/winfct.cpp
|
||||
@ -53,6 +53,7 @@ FILES
|
||||
yycc/macro/class_copy_move.hpp
|
||||
yycc/macro/printf_checker.hpp
|
||||
yycc/flag_enum.hpp
|
||||
yycc/string.hpp
|
||||
yycc/string/reinterpret.hpp
|
||||
yycc/string/op.hpp
|
||||
yycc/patch/ptr_pad.hpp
|
||||
@ -64,12 +65,12 @@ FILES
|
||||
yycc/num/safe_cast.hpp
|
||||
yycc/num/safe_op.hpp
|
||||
yycc/num/op.hpp
|
||||
yycc/rust/prelude.hpp
|
||||
yycc/rust/primitive.hpp
|
||||
yycc/rust/panic.hpp
|
||||
yycc/rust/option.hpp
|
||||
yycc/rust/result.hpp
|
||||
yycc/rust/env.hpp
|
||||
yycc/primitive.hpp
|
||||
yycc/option.hpp
|
||||
yycc/result.hpp
|
||||
yycc/prelude.hpp
|
||||
yycc/panic.hpp
|
||||
yycc/env.hpp
|
||||
yycc/windows/import_guard_head.hpp
|
||||
yycc/windows/import_guard_tail.hpp
|
||||
yycc/windows/com.hpp
|
||||
@ -95,6 +96,7 @@ FILES
|
||||
yycc/carton/clap/summary.hpp
|
||||
yycc/carton/clap/application.hpp
|
||||
yycc/carton/clap/manual.hpp
|
||||
yycc/carton/fft.hpp
|
||||
)
|
||||
# Setup header infomations
|
||||
target_include_directories(YYCCommonplace
|
||||
|
@ -3,12 +3,15 @@
|
||||
#include "../../patch/stream.hpp"
|
||||
#include "../../patch/format.hpp"
|
||||
#include "../../string/op.hpp"
|
||||
#include "../../env.hpp"
|
||||
#include <ranges>
|
||||
|
||||
#define CLAP ::yycc::carton::clap
|
||||
#define TABULATE ::yycc::carton::tabulate
|
||||
#define TERMCOLOR ::yycc::carton::termcolor
|
||||
#define OP ::yycc::string::op
|
||||
#define FORMAT ::yycc::patch::format
|
||||
#define OP ::yycc::carton::op
|
||||
#define ENV ::yycc::env
|
||||
|
||||
using namespace ::yycc::patch::stream;
|
||||
|
||||
@ -53,10 +56,45 @@ namespace yycc::carton::clap::manual {
|
||||
const auto &options = app.get_options();
|
||||
for (const auto ®_opt : options.all_options()) {
|
||||
const auto &opt = reg_opt.get_option();
|
||||
|
||||
auto desc_by_line = OP::lazy_split(opt.get_description(), u8"\n");
|
||||
for (const auto [index, item] : std::views::enumerate(desc_by_line)) {
|
||||
if (index == 0) {
|
||||
auto full_name = TERMCOLOR::colored(opt.to_showcase_name(),
|
||||
TERMCOLOR::Color::LightYellow,
|
||||
TERMCOLOR::Color::Default,
|
||||
TERMCOLOR::Attribute::Default);
|
||||
auto value_hint = TERMCOLOR::colored(opt.to_showcase_value(),
|
||||
TERMCOLOR::Color::LightGreen,
|
||||
TERMCOLOR::Color::Default,
|
||||
TERMCOLOR::Attribute::Default);
|
||||
this->opt_printer.add_row({full_name, value_hint, item});
|
||||
} else {
|
||||
this->opt_printer.add_row({u8"", u8"", item});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Manual::fill_var_table() {}
|
||||
void Manual::fill_var_table() {
|
||||
const auto &variables = app.get_variables();
|
||||
for (const auto ®_var : variables.all_variables()) {
|
||||
const auto &var = reg_var.get_variable();
|
||||
|
||||
auto desc_by_line = OP::lazy_split(var.get_description(), u8"\n");
|
||||
for (const auto [index, item] : std::views::enumerate(desc_by_line)) {
|
||||
if (index == 0) {
|
||||
auto name = TERMCOLOR::colored(var.get_name(),
|
||||
TERMCOLOR::Color::LightYellow,
|
||||
TERMCOLOR::Color::Default,
|
||||
TERMCOLOR::Attribute::Default);
|
||||
this->var_printer.add_row({name, item});
|
||||
} else {
|
||||
this->var_printer.add_row({u8"", item});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Manual::print_version(std::ostream &dst) const {
|
||||
const auto &summary = this->app.get_summary();
|
||||
@ -71,8 +109,12 @@ namespace yycc::carton::clap::manual {
|
||||
void Manual::print_help(std::ostream &dst) const {
|
||||
this->print_version();
|
||||
|
||||
TERMCOLOR::cprintln(trctx.usage_title, TERMCOLOR::Color::Yellow, TERMCOLOR::Color::Default, TERMCOLOR::Attribute::Default, dst);
|
||||
dst << INDENT << FORMAT::format(trctx.usage_body, app.get_summary().get_bin_name()) << std::endl;
|
||||
// only print usage if we can fetch the name of executable
|
||||
auto executable = ENV::current_exe();
|
||||
if (executable.has_value()) {
|
||||
TERMCOLOR::cprintln(trctx.usage_title, TERMCOLOR::Color::Yellow, TERMCOLOR::Color::Default, TERMCOLOR::Attribute::Default, dst);
|
||||
dst << INDENT << FORMAT::format(trctx.usage_body, executable.value()) << std::endl;
|
||||
}
|
||||
|
||||
const auto &variables = app.get_variables();
|
||||
if (!variables.empty()) {
|
||||
|
@ -3,11 +3,9 @@
|
||||
namespace yycc::carton::clap::summary {
|
||||
|
||||
Summary::Summary(const std::u8string_view &name,
|
||||
const std::u8string_view &bin_name,
|
||||
const std::u8string_view &author,
|
||||
const std::u8string_view &version,
|
||||
const std::u8string_view &description) :
|
||||
name(name), bin_name(bin_name), author(author), version(version), description(description) {}
|
||||
const std::u8string_view &description) : name(name), author(author), version(version), description(description) {}
|
||||
|
||||
Summary::~Summary() {}
|
||||
|
||||
@ -15,10 +13,6 @@ namespace yycc::carton::clap::summary {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
std::u8string_view Summary::get_bin_name() const {
|
||||
return this->bin_name;
|
||||
}
|
||||
|
||||
std::u8string_view Summary::get_author() const {
|
||||
return this->author;
|
||||
}
|
||||
|
@ -4,11 +4,10 @@
|
||||
#include <string_view>
|
||||
|
||||
namespace yycc::carton::clap::summary {
|
||||
|
||||
|
||||
class Summary {
|
||||
public:
|
||||
Summary(const std::u8string_view& name,
|
||||
const std::u8string_view& bin_name,
|
||||
const std::u8string_view& author,
|
||||
const std::u8string_view& version,
|
||||
const std::u8string_view& description);
|
||||
@ -17,13 +16,12 @@ namespace yycc::carton::clap::summary {
|
||||
|
||||
public:
|
||||
std::u8string_view get_name() const;
|
||||
std::u8string_view get_bin_name() const;
|
||||
std::u8string_view get_author() const;
|
||||
std::u8string_view get_version() const;
|
||||
std::u8string_view get_description() const;
|
||||
|
||||
private:
|
||||
std::u8string name, bin_name, author, version, description;
|
||||
std::u8string name, author, version, description;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace yycc::carton::clap::summary
|
||||
|
307
src/yycc/carton/fft.hpp
Normal file
307
src/yycc/carton/fft.hpp
Normal file
@ -0,0 +1,307 @@
|
||||
#pragma once
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
#include <numbers>
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <complex>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
namespace yycc::carton::fft {
|
||||
|
||||
/// @private
|
||||
/// @brief Meta-programming utilities for FFT modules.
|
||||
namespace util {
|
||||
|
||||
template<std::floating_point TFloat>
|
||||
inline constexpr TFloat tau_v = static_cast<TFloat>(2) * std::numbers::pi_v<TFloat>;
|
||||
|
||||
// NOTE:
|
||||
// We use std::has_single_bit() to check whether given number is an integral power of 2.
|
||||
// And use (std::bit_width() - 1) to get the exponent of given number based on 2.
|
||||
|
||||
template<typename TIndex, typename TFloat, size_t N>
|
||||
struct validate_args {
|
||||
private:
|
||||
static constexpr bool is_unsigned_int = std::is_unsigned_v<TIndex> && std::is_integral_v<TIndex>;
|
||||
static constexpr bool is_float_point = std::is_floating_point_v<TFloat>;
|
||||
static constexpr bool n_is_pow_2 = std::has_single_bit<TIndex>(static_cast<TIndex>(N)) && N >= static_cast<TIndex>(2);
|
||||
|
||||
public:
|
||||
static constexpr bool value = is_unsigned_int && is_float_point && n_is_pow_2;
|
||||
};
|
||||
|
||||
template<typename TIndex, typename TFloat, size_t N>
|
||||
inline constexpr bool validate_args_v = validate_args<TIndex, TFloat, N>::value;
|
||||
|
||||
} // namespace util
|
||||
|
||||
#pragma region Window
|
||||
|
||||
enum class WindowType { HanningWindow };
|
||||
|
||||
template<typename TIndex, typename TFloat, size_t N>
|
||||
requires util::validate_args_v<TIndex, TFloat, N>
|
||||
class Window {
|
||||
private:
|
||||
static constexpr TIndex N = N;
|
||||
|
||||
public:
|
||||
Window(WindowType win_type) : window_type(win_type), window_data(nullptr) {
|
||||
// Pre-compute window data
|
||||
// Allocate window buffer
|
||||
window_data = std::make_unique<TFloat[]>(N);
|
||||
// Assign window data
|
||||
switch (win_type) {
|
||||
case WindowType::HanningWindow:
|
||||
for (TIndex i = 0u; i < N; ++i) {
|
||||
window_data[i] = static_cast<TFloat>(0.5)
|
||||
* (static_cast<TFloat>(1)
|
||||
- std::cos(util::tau_v<TFloat>
|
||||
* static_cast<TFloat>(i) / static_cast<TFloat>(N - static_cast<TIndex>(1))));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw std::invalid_argument("invalid window function type");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
WindowType window_type;
|
||||
std::unique_ptr<TFloat[]> window_data;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Apply window function to given data sequence.
|
||||
* @param[in,out] data
|
||||
* The float-point data sequence for applying window function.
|
||||
* The length of this sequence must be N.
|
||||
*/
|
||||
void apply_window(TFloat* data) const {
|
||||
if (data == nullptr) [[unlikely]] {
|
||||
throw std::invalid_argument("nullptr data is not allowed for applying window.");
|
||||
}
|
||||
for (TIndex i = static_cast<TIndex>(0); i < N; ++i) {
|
||||
data[i] *= window_data[i];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Get underlying window function data for custom applying.
|
||||
* @return
|
||||
* The pointer to the start address of underlying window function data sequence.
|
||||
* The length of this sequence is N.
|
||||
*/
|
||||
const TFloat* get_window_data() const { return window_data.get(); }
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region FFT
|
||||
|
||||
template<typename TIndex, typename TFloat, size_t N>
|
||||
requires util::validate_args_v<TIndex, TFloat, N>
|
||||
struct FftProperties {
|
||||
public:
|
||||
using TComplex = std::complex<TFloat>;
|
||||
static constexpr TIndex N = static_cast<TIndex>(N);
|
||||
static constexpr TIndex M = static_cast<TIndex>(std::bit_width<TIndex>(N) - 1);
|
||||
static constexpr TIndex HALF_POINT = N >> static_cast<TIndex>(1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The core FFT class.
|
||||
* @details The core class implementing FFT algorithm (base-2 version).
|
||||
* @tparam TIndex
|
||||
* @tparam TFloat
|
||||
* @tparam N
|
||||
*/
|
||||
template<typename TIndex, typename TFloat, size_t N>
|
||||
requires util::validate_args_v<TIndex, TFloat, N>
|
||||
class Fft {
|
||||
private:
|
||||
using TProperties = FftProperties<TIndex, TFloat, N>;
|
||||
using TComplex = TProperties::TComplex;
|
||||
static constexpr TIndex N = TProperties::N;
|
||||
static constexpr TIndex M = TProperties::M;
|
||||
static constexpr TIndex HALF_POINT = TProperties::HALF_POINT;
|
||||
|
||||
public:
|
||||
Fft() : wnp_cache(nullptr) {
|
||||
// Generate WNP cache
|
||||
wnp_cache = std::make_unique<TComplex[]>(N);
|
||||
for (TIndex P = static_cast<TIndex>(0); P < N; ++P) {
|
||||
TFloat angle = util::tau_v<TFloat> * static_cast<TFloat>(P) / static_cast<TFloat>(N);
|
||||
// e^(-jx) = cosx - j sinx
|
||||
wnp_cache[P] = TComplex(std::cos(angle), -std::sin(angle));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<TComplex[]> wnp_cache;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Compute FFT for given complex sequence.
|
||||
* @details
|
||||
* This is FFT core compute function but not suit for common user
|
||||
* because it order that you have enough FFT knowledge to understand what is input data and what is output data.
|
||||
* For convenient use, see also easy_compute().
|
||||
* @param[in,out] data
|
||||
* The complex sequence for computing.
|
||||
* The length of this sequence must be N.
|
||||
*/
|
||||
void compute(TComplex* data) const {
|
||||
if (data == nullptr) [[unlikely]] {
|
||||
throw std::invalid_argument("nullptr data is not allowed for FFT computing.");
|
||||
}
|
||||
|
||||
TIndex LH, J, K, B, P;
|
||||
LH = J = HALF_POINT;
|
||||
|
||||
// Construct butterfly structure
|
||||
for (TIndex I = static_cast<TIndex>(1); I <= N - static_cast<TIndex>(2); ++I) {
|
||||
if (I < J) std::swap(data[I], data[J]);
|
||||
|
||||
K = LH;
|
||||
while (J >= K) {
|
||||
J -= K;
|
||||
K >>= static_cast<TIndex>(1);
|
||||
}
|
||||
J += K;
|
||||
}
|
||||
|
||||
// Calculate butterfly
|
||||
TComplex temp, temp2;
|
||||
for (TIndex L = static_cast<TIndex>(1); L <= M; ++L) {
|
||||
B = static_cast<TIndex>(1u) << (L - static_cast<TIndex>(1));
|
||||
for (J = static_cast<TIndex>(0); J <= B - static_cast<TIndex>(1); ++J) {
|
||||
P = J * (static_cast<TIndex>(1) << (M - L));
|
||||
|
||||
// Use pre-computed cache instead of real-time computing
|
||||
for (TIndex KK = J; KK <= N - static_cast<TIndex>(1); KK += (static_cast<TIndex>(1) << L)) {
|
||||
temp2 = (data[KK + B] * this->wnp_cache[P]);
|
||||
temp = temp2 + data[KK];
|
||||
data[KK + B] = data[KK] - temp2;
|
||||
data[KK] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief User friendly FFT computation class.
|
||||
* @details
|
||||
* @tparam TIndex
|
||||
* @tparam TFloat
|
||||
* @tparam N
|
||||
* @warning This class is \b NOT thread safe. Please use different instance in different thread.
|
||||
*/
|
||||
template<typename TIndex, typename TFloat, size_t N>
|
||||
requires util::validate_args_v<TIndex, TFloat, N>
|
||||
class FriendlyFft {
|
||||
private:
|
||||
using UnderlyingFft = Fft<TIndex, TFloat, N>;
|
||||
using TProperties = FftProperties<TIndex, TFloat, N>;
|
||||
using TComplex = TProperties::TComplex;
|
||||
static constexpr TIndex N = TProperties::N;
|
||||
static constexpr TIndex M = TProperties::M;
|
||||
static constexpr TIndex HALF_POINT = TProperties::HALF_POINT;
|
||||
|
||||
public:
|
||||
FriendlyFft() : compute_cache(N) {
|
||||
// Initialize computation used buffer.
|
||||
compute_cache = std::vector<TComplex>();
|
||||
}
|
||||
|
||||
private:
|
||||
UnderlyingFft underlying_fft;
|
||||
std::vector<TComplex> compute_cache;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get the maximum frequency by given sample rate.
|
||||
* @param[in] sample_rate
|
||||
* The sample rate of input stream.
|
||||
* Unit is Hz or SPS (sample point per second).
|
||||
* @return
|
||||
* The last data in computed FFT drequency data represented frequency.
|
||||
* Unit is Hz.
|
||||
*/
|
||||
TFloat get_max_freq(TFloat sample_rate) {
|
||||
// Following sample priniciple
|
||||
return sample_rate / static_cast<TFloat>(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compute FFT for given time scope data.
|
||||
* @details
|
||||
* This is convenient FFT compute function, comparing with compute().
|
||||
* This function accepts time scope data and output frequency scope data automatically.
|
||||
* Additionally, it order a window function instance to apply to time scope data before computing.
|
||||
* @param[in] time_scope The length of this data must be N.
|
||||
* For the time order of data, the first data should be the oldest data and the last data should be the newest data.
|
||||
* @param[out] freq_scope The length of this data must be N / 2.
|
||||
* The first data is 0Hz and the frequency of last data is decided by sample rate which can be computed by get_max_freq() function in this class.
|
||||
* @param[in] window The window instance applied to data.
|
||||
* @warnings
|
||||
* This function is \b NOT thread-safe.
|
||||
* Please do NOT call this function in different thread for one instance.
|
||||
*/
|
||||
void easy_compute(const TFloat* time_scope, TFloat* freq_scope, const Window<TIndex, TFloat, N>& window) {
|
||||
if (time_scope == nullptr || freq_scope == nullptr) [[unlikely]] {
|
||||
throw std::invalid_argument("nullptr data is not allowed for easy FFT computing.");
|
||||
}
|
||||
|
||||
// First, we copy time scope data into cache with reversed order.
|
||||
// because FFT order the first item should be the latest data.
|
||||
// At the same time we multiple it with window function.
|
||||
std::generate(compute_cache.begin(),
|
||||
compute_cache.end(),
|
||||
[data = &(time_scope[N]), win_data = window.get_window_data()]() mutable -> TComplex {
|
||||
return TComplex(*(data--) * *(win_data++));
|
||||
});
|
||||
|
||||
// Do FFT compute
|
||||
underlying_fft.compute(compute_cache.data());
|
||||
|
||||
// Compute amplitude
|
||||
for (TIndex i = static_cast<TIndex>(0); i < HALF_POINT; ++i) {
|
||||
freq_scope[i] = static_cast<TFloat>(10) * std::log10(std::abs(compute_cache[i + HALF_POINT]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Pre-defined FFT Types
|
||||
|
||||
using Fft4F = Fft<size_t, float, 4u>;
|
||||
using Fft8F = Fft<size_t, float, 8u>;
|
||||
using Fft16F = Fft<size_t, float, 16u>;
|
||||
using Fft32F = Fft<size_t, float, 32u>;
|
||||
using Fft64F = Fft<size_t, float, 64u>;
|
||||
using Fft128F = Fft<size_t, float, 128u>;
|
||||
using Fft256F = Fft<size_t, float, 256u>;
|
||||
using Fft512F = Fft<size_t, float, 512u>;
|
||||
using Fft1024F = Fft<size_t, float, 1024u>;
|
||||
using Fft2048F = Fft<size_t, float, 2048u>;
|
||||
|
||||
using Fft4 = Fft<size_t, double, 4u>;
|
||||
using Fft8 = Fft<size_t, double, 8u>;
|
||||
using Fft16 = Fft<size_t, double, 16u>;
|
||||
using Fft32 = Fft<size_t, double, 32u>;
|
||||
using Fft64 = Fft<size_t, double, 64u>;
|
||||
using Fft128 = Fft<size_t, double, 128u>;
|
||||
using Fft256 = Fft<size_t, double, 256u>;
|
||||
using Fft512 = Fft<size_t, double, 512u>;
|
||||
using Fft1024 = Fft<size_t, double, 1024u>;
|
||||
using Fft2048 = Fft<size_t, double, 2048u>;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::carton::fft
|
@ -1,27 +1,39 @@
|
||||
#include "env.hpp"
|
||||
#include "../macro/os_detector.hpp"
|
||||
#include "macro/os_detector.hpp"
|
||||
|
||||
// Environment variable required
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
#include "../encoding/windows.hpp"
|
||||
#include "../num/safe_op.hpp"
|
||||
#include "../num/safe_cast.hpp"
|
||||
#include "encoding/windows.hpp"
|
||||
#include "num/safe_op.hpp"
|
||||
#include "num/safe_cast.hpp"
|
||||
#include <Windows.h>
|
||||
#include <winbase.h>
|
||||
#else
|
||||
#include "../string/reinterpret.hpp"
|
||||
#include "string/reinterpret.hpp"
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <stdexcept>
|
||||
#endif
|
||||
|
||||
// Path related functions required
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
#include "windows/winfct.hpp"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#define SAFECAST ::yycc::num::safe_cast
|
||||
#define SAFEOP ::yycc::num::safe_op
|
||||
#define ENC ::yycc::encoding::windows
|
||||
#define REINTERPRET ::yycc::string::reinterpret
|
||||
#define WINFCT ::yycc::windows::winfct
|
||||
|
||||
namespace yycc::rust::env {
|
||||
namespace yycc::env {
|
||||
|
||||
EnvResult<std::u8string> get_var(const std::u8string_view &name) {
|
||||
#pragma region Environment Variable
|
||||
|
||||
VarResult<std::u8string> get_var(const std::u8string_view &name) {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
|
||||
// Convert to wchar
|
||||
@ -38,22 +50,22 @@ namespace yycc::rust::env {
|
||||
// the size passed to this function must include NULL terminal.
|
||||
// So we forcely use checked add and sub for this bad behavior.
|
||||
auto fct_size = SAFEOP::checked_add<size_t>(wvalue.size(), 1);
|
||||
if (!fct_size.has_value()) return std::unexpected(EnvError::BadArithmetic);
|
||||
if (!fct_size.has_value()) return std::unexpected(VarError::BadArithmetic);
|
||||
auto rv = ::GetEnvironmentVariableW(wname.c_str(), wvalue.data(), fct_size.value());
|
||||
|
||||
// Check the return value
|
||||
if (rv == 0) {
|
||||
// Function failed. Extract error reason.
|
||||
auto ec = GetLastError();
|
||||
if (ec == ERROR_ENVVAR_NOT_FOUND) return std::unexpected(EnvError::NoSuchName);
|
||||
else return std::unexpected(EnvError::BadCall);
|
||||
if (ec == ERROR_ENVVAR_NOT_FOUND) return std::unexpected(VarError::NoSuchName);
|
||||
else return std::unexpected(VarError::BadCall);
|
||||
} else {
|
||||
// Function okey. Check the size.
|
||||
// Fetch function expected size.
|
||||
auto rv_size = SAFECAST::try_to<size_t>(rv);
|
||||
if (!rv_size.has_value()) return std::unexpected(EnvError::BadArithmetic);
|
||||
if (!rv_size.has_value()) return std::unexpected(VarError::BadArithmetic);
|
||||
auto exp_size = SAFEOP::checked_sub<size_t>(rv_size.value(), 1);
|
||||
if (!exp_size.has_value()) return std::unexpected(EnvError::BadArithmetic);
|
||||
if (!exp_size.has_value()) return std::unexpected(VarError::BadArithmetic);
|
||||
|
||||
// YYC MARK:
|
||||
// According to Microsoft, the return value of this function is just a bullshit.
|
||||
@ -75,7 +87,7 @@ namespace yycc::rust::env {
|
||||
}
|
||||
|
||||
// Convert back to UTF8 string and return.
|
||||
return ENC::to_utf8(wvalue).transform_error([](auto err) { return EnvError::BadEncoding; });
|
||||
return ENC::to_utf8(wvalue).transform_error([](auto err) { return VarError::BadEncoding; });
|
||||
#else
|
||||
// String view is not NULL-terminal-guaranted,
|
||||
// so we solve this when casting its type.
|
||||
@ -83,18 +95,18 @@ namespace yycc::rust::env {
|
||||
|
||||
// Fetch variable
|
||||
auto finder = std::getenv(ordinary_name.c_str());
|
||||
if (finder == nullptr) return std::unexpected(EnvError::NoSuchName);
|
||||
if (finder == nullptr) return std::unexpected(VarError::NoSuchName);
|
||||
else return REINTERPRET::as_utf8(finder);
|
||||
#endif
|
||||
}
|
||||
|
||||
EnvResult<void> set_var(const std::u8string_view &name, const std::u8string_view &value) {
|
||||
VarResult<void> set_var(const std::u8string_view &name, const std::u8string_view &value) {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
||||
|
||||
// Convert to wchar, set variable, and check result.
|
||||
auto rv = ::SetEnvironmentVariableW(ENC::to_wchar(name).value().c_str(), ENC::to_wchar(value).value().c_str());
|
||||
if (!rv) return std::unexpected(EnvError::BadCall);
|
||||
if (!rv) return std::unexpected(VarError::BadCall);
|
||||
else return {};
|
||||
#else
|
||||
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html
|
||||
@ -106,19 +118,19 @@ namespace yycc::rust::env {
|
||||
if (rv == 0) return {};
|
||||
|
||||
// Check error type
|
||||
if (errno == EINVAL) return std::unexpected(EnvError::BadName);
|
||||
else if (errno == ENOMEM) return std::unexpected(EnvError::NoMemory);
|
||||
if (errno == EINVAL) return std::unexpected(VarError::BadName);
|
||||
else if (errno == ENOMEM) return std::unexpected(VarError::NoMemory);
|
||||
else throw std::runtime_error("impossible errno");
|
||||
#endif
|
||||
}
|
||||
|
||||
EnvResult<void> del_var(const std::u8string_view &name) {
|
||||
VarResult<void> del_var(const std::u8string_view &name) {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
||||
|
||||
// Convert to wchar, delete variable, and check result.
|
||||
auto rv = ::SetEnvironmentVariableW(ENC::to_wchar(name).value().c_str(), NULL);
|
||||
if (!rv) return std::unexpected(EnvError::BadCall);
|
||||
if (!rv) return std::unexpected(VarError::BadCall);
|
||||
else return {};
|
||||
#else
|
||||
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/unsetenv.html
|
||||
@ -129,9 +141,70 @@ namespace yycc::rust::env {
|
||||
if (rv == 0) return {};
|
||||
|
||||
// Check error type
|
||||
if (errno == EINVAL) return std::unexpected(EnvError::BadName);
|
||||
if (errno == EINVAL) return std::unexpected(VarError::BadName);
|
||||
else throw std::runtime_error("impossible errno");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace yycc::rust::env
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Environment
|
||||
|
||||
PathResult<std::u8string> current_exe() {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
return WINFCT::get_module_file_name(NULL).transform_error([](auto e) { return PathError::Win32; });
|
||||
#else
|
||||
// TODO:
|
||||
// "/proc/self/exe" is Linux specific, not in POSIX standard.
|
||||
// This method may need further patch when running on macOS.
|
||||
|
||||
// Reference: https://www.man7.org/linux/man-pages/man2/readlink.2.html
|
||||
|
||||
// specify the path
|
||||
constexpr char path[] = "/proc/self/exe";
|
||||
|
||||
// get the expected size
|
||||
struct stat sb;
|
||||
if (lstat(path, &sb) != 0) {
|
||||
}
|
||||
auto expected_size = SAFECAST::try_to<size_t>(sb.st_size);
|
||||
if (!expected_size.has_value()) {
|
||||
return std::unexpected(PathError::BadCast);
|
||||
}
|
||||
auto buf_size = expected_size.value();
|
||||
// Some magic symlinks under (for example) /proc and /sys report 'st_size' as zero.
|
||||
// In that case, take PATH_MAX as a "good enough" estimate.
|
||||
if (buf_size == 0) {
|
||||
buf_size = PATH_MAX;
|
||||
}
|
||||
|
||||
// prepare buffer and resize it;
|
||||
std::u8string rv(u8'\0', buf_size);
|
||||
|
||||
// write data
|
||||
auto passed_size = SAFEOP::checked_add<size_t>(buf_size, 1);
|
||||
if (!passed_size.has_value()) {
|
||||
return std::unexpected(PathError::Overflow);
|
||||
}
|
||||
ssize_t nbytes = readlink(path, REINTERPRET::as_ordinary(rv.data()), passed_size.value());
|
||||
if (nbytes < 0) {
|
||||
return std::unexpected(PathError::Posix);
|
||||
}
|
||||
|
||||
// check written size
|
||||
auto written_size = SAFECAST::try_to<size_t>(nbytes);
|
||||
if (!written_size.has_value()) {
|
||||
return std::unexpected(PathError::BadCast);
|
||||
}
|
||||
if (written_size.value() != buf_size) {
|
||||
return std::unexpected(PathError::BadSize);
|
||||
}
|
||||
|
||||
// okey
|
||||
return rv;
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::env
|
89
src/yycc/env.hpp
Normal file
89
src/yycc/env.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <expected>
|
||||
|
||||
/**
|
||||
* @brief The namespace providing runtime environment operations.
|
||||
|
||||
* @details
|
||||
* When I programming with Rust, I was astonished that
|
||||
* Rust standard library have so much robust environment-related operations,
|
||||
* such as environment variable operations, current program infos and etc.
|
||||
* Oppositly, C++ STL are still lack in this even in today.
|
||||
* So I create this namespace to glue all these things up,
|
||||
* according to different operating systems, and make a uniform interface.
|
||||
*/
|
||||
namespace yycc::env {
|
||||
|
||||
#pragma region Environment Variable
|
||||
|
||||
/// @brief The error occurs in environment variable operations.
|
||||
enum class VarError {
|
||||
NoSuchName, ///< The variable with given name is not presented.
|
||||
BadEncoding, ///< Error when performing encoding convertion.
|
||||
BadArithmetic, ///< Error when performing arithmetic operations.
|
||||
BadCall, ///< Error occurs when calling backend functions.
|
||||
BadName, ///< Given name is ill-formated (empty string or has "=" character).
|
||||
NoMemory, ///< No enough memory to finish this operation.
|
||||
};
|
||||
|
||||
/// @brief The result type in environment variable operations.
|
||||
template<typename T>
|
||||
using VarResult = std::expected<T, VarError>;
|
||||
|
||||
/**
|
||||
* @brief Get the value of given environment variable name.
|
||||
* @param[in] name The name of environment variable
|
||||
* @return Gotten value, or error occurs.
|
||||
*/
|
||||
VarResult<std::u8string> get_var(const std::u8string_view& name);
|
||||
|
||||
/**
|
||||
* @brief Set the value of given environment variable name.
|
||||
* @details
|
||||
* If there is no such name variable presented in environment,
|
||||
* a new variable will be created,
|
||||
* otherwise, new value will overwrite old value.
|
||||
* @param[in] name The name of environment variable
|
||||
* @param[in] value The value to be written into.
|
||||
* @return Nothing or error occurs.
|
||||
*/
|
||||
VarResult<void> set_var(const std::u8string_view& name, const std::u8string_view& value);
|
||||
|
||||
/**
|
||||
* @brief Delete environment variable with given name.
|
||||
* @details
|
||||
* If given variable is not presented in environment,
|
||||
* this function will NOT return error.
|
||||
* @param[in] name The name of environment variable
|
||||
* @return Nothing, or error occurs.
|
||||
*/
|
||||
VarResult<void> del_var(const std::u8string_view& name);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Environment Path
|
||||
|
||||
/// @brief Error occurs when operating path related functions.
|
||||
enum class PathError {
|
||||
Win32, ///< Underlying Win32 function error.
|
||||
Posix, ///< Underlying POSIX failed.
|
||||
BadSize, ///< Written size if not matched with expected size.
|
||||
BadCast, ///< Error occurs when casting values.
|
||||
Overflow, ///< Some arithmetic operation overflow.
|
||||
};
|
||||
|
||||
/// @brief The result type used for path related functions;
|
||||
template<typename T>
|
||||
using PathResult = std::expected<T, PathError>;
|
||||
|
||||
/**
|
||||
* @brief Get the path of the current running executable.
|
||||
* @return Gotten path (no absolute path guaranteed) or error occurs.
|
||||
*/
|
||||
PathResult<std::u8string> current_exe();
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::env
|
@ -7,7 +7,7 @@
|
||||
* This namespace reproduce Rust Option type, and its members Some and None in C++.
|
||||
* However Option is not important than Result, so its implementation is very casual.
|
||||
*/
|
||||
namespace yycc::rust::option {
|
||||
namespace yycc::option {
|
||||
|
||||
template<typename T>
|
||||
using Option = std::optional<T>;
|
||||
@ -22,4 +22,4 @@ namespace yycc::rust::option {
|
||||
return OptionType(std::nullopt);
|
||||
}
|
||||
|
||||
} // namespace yycc::rust::option
|
||||
} // namespace yycc::option
|
@ -1,6 +1,6 @@
|
||||
#include "panic.hpp"
|
||||
#include "../carton/termcolor.hpp"
|
||||
#include "../patch/stream.hpp"
|
||||
#include "carton/termcolor.hpp"
|
||||
#include "patch/stream.hpp"
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
using namespace yycc::patch::stream;
|
||||
|
||||
namespace yycc::rust::panic {
|
||||
namespace yycc::panic {
|
||||
|
||||
void panic(const char* file, int line, const std::u8string_view& msg) {
|
||||
// Output message in stderr.
|
||||
@ -36,4 +36,4 @@ namespace yycc::rust::panic {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
} // namespace yycc::rust::panic
|
||||
} // namespace yycc::panic
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "../patch/format.hpp"
|
||||
#include "patch/format.hpp"
|
||||
#include <string_view>
|
||||
#include <format>
|
||||
|
||||
@ -15,13 +15,16 @@
|
||||
*
|
||||
* Unfortunately, I cannot change the exception mechanism in the standard library.
|
||||
* The standard library will still throw exceptions where it does, and I cannot prevent that.
|
||||
* Therefore, I suggest a good practice that any C++ exception should be immediately treated as an error and cause the program to crash and exit.
|
||||
* For this reason, registering any unhandled error callbacks which may resume the execution of program is prohibited to prevent unexpected continuation of execution.
|
||||
* For code we write ourselves that we can control, we should use the macros provided in this file instead of throwing exceptions.
|
||||
* In this way, unexpected behavior in our code will cause the program to exit immediately, outputting error information and stack traces.
|
||||
* Therefore, I suggest a good practice call "exception is error",
|
||||
* any C++ exception should be immediately treated as an error and cause the program to crash and exit.
|
||||
* For this reason, registering any unhandled error callbacks which may resume the execution of program
|
||||
* is strictly prohibited to prevent any unexpected recovery from exceptions.
|
||||
* For code your written, you should use the macros provided in this file instead of throwing exceptions.
|
||||
* In this way, unexpected behavior in code will cause immediate exit, shown error information and stack traces.
|
||||
* Standard library exceptions will also cause the program to exit, but without stack information.
|
||||
* However, if you are following "exception is error" rule, you still can throw exceptions instead.
|
||||
*/
|
||||
namespace yycc::rust::panic {
|
||||
namespace yycc::panic {
|
||||
|
||||
/**
|
||||
* @brief Immediately crashes the entire program like Rust's \c panic! macro.
|
||||
@ -31,7 +34,7 @@ namespace yycc::rust::panic {
|
||||
* However, this format function is specially modified that it can accept UTF8 format string and UTF8 string argument.
|
||||
* More preciously, it is "format" in \c yycc::patch::format namespace.
|
||||
*/
|
||||
#define RS_PANIC(msg, ...) ::yycc::rust::panic::panic(__FILE__, __LINE__, ::yycc::patch::format::format(msg __VA_OPT__(, ) __VA_ARGS__))
|
||||
#define RS_PANIC(msg, ...) ::yycc::panic::panic(__FILE__, __LINE__, ::yycc::patch::format::format(msg __VA_OPT__(, ) __VA_ARGS__))
|
||||
|
||||
/**
|
||||
* @brief Immediately crashes the entire program like Rust's \c panic! macro.
|
||||
@ -44,4 +47,4 @@ namespace yycc::rust::panic {
|
||||
*/
|
||||
[[noreturn]] void panic(const char* file, int line, const std::u8string_view& msg);
|
||||
|
||||
} // namespace yycc::rust::panic
|
||||
} // namespace yycc::panic
|
68
src/yycc/prelude.hpp
Normal file
68
src/yycc/prelude.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file
|
||||
* @brief The Rust-like prelude header for C++.
|
||||
* @details
|
||||
* When I writting with Rust, I notice Rust add types for all files in default.
|
||||
* This default imported types are called "prelude".
|
||||
* This is very convenient for programming so I decide to introduce it in C++.
|
||||
*
|
||||
* I create this file, organize all types, which I think should be exposed for programmer, in to an independent namespace,
|
||||
* and expose them into global namesoace.
|
||||
* By simply include this file at the top of your C++ code, you can get Rust-like prelude effect in C++.
|
||||
* These exposed types including primitive types, string types, basic Option and Result utilities, panic mechanisim and etc.
|
||||
*/
|
||||
|
||||
// Rust prelude section
|
||||
#include "primitive.hpp"
|
||||
#include "result.hpp"
|
||||
#include "option.hpp"
|
||||
#include "panic.hpp"
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief The namespace including all types presented in prelude.
|
||||
* @details
|
||||
* By including this file, all of these types are automaticalling exposed to global namespace.
|
||||
* There is no need to refer this namespace anymore.
|
||||
* This namespace is just a container for types which need to be exposed.
|
||||
*/
|
||||
namespace yycc::prelude {
|
||||
// Include primitive types
|
||||
|
||||
#define NS_YYCC_PRIMITIVE ::yycc::primitive
|
||||
|
||||
using i8 = NS_YYCC_PRIMITIVE::i8;
|
||||
using i16 = NS_YYCC_PRIMITIVE::i16;
|
||||
using i32 = NS_YYCC_PRIMITIVE::i32;
|
||||
using i64 = NS_YYCC_PRIMITIVE::i64;
|
||||
using u8 = NS_YYCC_PRIMITIVE::u8;
|
||||
using u16 = NS_YYCC_PRIMITIVE::u16;
|
||||
using u32 = NS_YYCC_PRIMITIVE::u32;
|
||||
using u64 = NS_YYCC_PRIMITIVE::u64;
|
||||
|
||||
using isize = NS_YYCC_PRIMITIVE::isize;
|
||||
using usize = NS_YYCC_PRIMITIVE::usize;
|
||||
|
||||
using f32 = NS_YYCC_PRIMITIVE::f32;
|
||||
using f64 = NS_YYCC_PRIMITIVE::f64;
|
||||
|
||||
#undef NS_YYCC_PRIMITIVE
|
||||
|
||||
// Other types
|
||||
//using str = std::u8string_view;
|
||||
//using String = std::u8string;
|
||||
template<typename T>
|
||||
using Vec = std::vector<T>;
|
||||
|
||||
// Expose Result and Option
|
||||
using namespace ::yycc::option;
|
||||
using namespace ::yycc::result;
|
||||
|
||||
// Panic are introduced by including header file
|
||||
// so we do not need re-expose it.
|
||||
|
||||
} // namespace yycc::prelude
|
||||
|
||||
// Expose all members
|
||||
using namespace ::yycc::prelude;
|
40
src/yycc/primitive.hpp
Normal file
40
src/yycc/primitive.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
|
||||
/**
|
||||
* @brief The namespace providing primitive types.
|
||||
* @details
|
||||
* When I writing with Rust, I notice that most pf primitive types has explicit and exact size, and their names are short and clear.
|
||||
* Hoeever, in C++, most primitive types are variable based on system, due to the legacy of C era.
|
||||
* All primitive types with explicit size are only can be fetched in a specific header file,
|
||||
* and, their names are too long because they can not be registered as reserved keywords or names
|
||||
* due to the name conflict with so much code written in past years.
|
||||
* However, STL can't do this but I can do this.
|
||||
* I invent this namespace providing primitive types, such as integers, floating-point numbers, and strings,
|
||||
* in Rust style, their names areshort and clear.
|
||||
*/
|
||||
namespace yycc::primitive {
|
||||
|
||||
// `bool` is keyword so should not declare it anymore.
|
||||
// `char` is keyword so should not declare it anymore.
|
||||
|
||||
using i8 = std::int8_t;
|
||||
using i16 = std::int16_t;
|
||||
using i32 = std::int32_t;
|
||||
using i64 = std::int64_t;
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
using isize = std::ptrdiff_t;
|
||||
using usize = std::size_t;
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
|
||||
// using String = std::u8string;
|
||||
// using str = std::u8string_view;
|
||||
}
|
@ -36,7 +36,7 @@
|
||||
* Similarly, when using \c std::cerr 's \c operator<< overload, you also need to write suitable adapters.
|
||||
* @remarks This namespace only work with environment supporting `std::expected` (i.e. C++ 23).
|
||||
*/
|
||||
namespace yycc::rust::result {
|
||||
namespace yycc::result {
|
||||
|
||||
/**
|
||||
* @brief Equivalent Rust \c Result in C++
|
||||
@ -74,4 +74,4 @@ namespace yycc::rust::result {
|
||||
return ResultType(std::unexpect, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace yycc::rust::result
|
||||
} // namespace yycc::result
|
@ -1,62 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <expected>
|
||||
|
||||
/**
|
||||
* @brief The namespace providing environment variable operations.
|
||||
* @details
|
||||
* When I programming with Rust, I was astonished that
|
||||
* Rust standard library have so much robust environment variable operations.
|
||||
* Oppositly, C++ STL still lake in this even in today.
|
||||
*
|
||||
* The functions manipulating environment variable is different in different OS.
|
||||
* I create this namespace inspired from Rust standard library
|
||||
* to glue all these things up and make a uniform interface.
|
||||
*/
|
||||
namespace yycc::rust::env {
|
||||
|
||||
/// @brief The error occurs in this module.
|
||||
enum class EnvError {
|
||||
NoSuchName, ///< The variable with given name is not presented.
|
||||
BadEncoding, ///< Error when performing encoding convertion.
|
||||
BadArithmetic, ///< Error when performing arithmetic operations.
|
||||
BadCall, ///< Error occurs when calling backend functions.
|
||||
BadName, ///< Given name is ill-formated (empty string or has "=" character).
|
||||
NoMemory, ///< No enough memory to finish this operation.
|
||||
};
|
||||
|
||||
/// @brief The result type in this module.
|
||||
template<typename T>
|
||||
using EnvResult = std::expected<T, EnvError>;
|
||||
|
||||
/**
|
||||
* @brief Get the value of given environment variable name.
|
||||
* @param[in] name The name of environment variable
|
||||
* @return Gotten value, or error occurs.
|
||||
*/
|
||||
EnvResult<std::u8string> get_var(const std::u8string_view& name);
|
||||
|
||||
/**
|
||||
* @brief Set the value of given environment variable name.
|
||||
* @details
|
||||
* If there is no such name variable presented in environment,
|
||||
* a new variable will be created,
|
||||
* otherwise, new value will overwrite old value.
|
||||
* @param[in] name The name of environment variable
|
||||
* @param[in] value The value to be written into.
|
||||
* @return Nothing or error occurs.
|
||||
*/
|
||||
EnvResult<void> set_var(const std::u8string_view& name, const std::u8string_view& value);
|
||||
|
||||
/**
|
||||
* @brief Delete environment variable with given name.
|
||||
* @details
|
||||
* If given variable is not presented in environment,
|
||||
* this function will NOT return error.
|
||||
* @param[in] name The name of environment variable
|
||||
* @return Nothing, or error occurs.
|
||||
*/
|
||||
EnvResult<void> del_var(const std::u8string_view& name);
|
||||
|
||||
} // namespace yycc::rust::env
|
@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Rust prelude section
|
||||
#include "primitive.hpp"
|
||||
#include "result.hpp"
|
||||
#include "option.hpp"
|
||||
#include "panic.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace yycc::rust::prelude {
|
||||
// Include primitive types
|
||||
|
||||
#define NS_RUST_PRIMITIVE ::yycc::rust::primitive
|
||||
|
||||
using i8 = NS_RUST_PRIMITIVE::i8;
|
||||
using i16 = NS_RUST_PRIMITIVE::i16;
|
||||
using i32 = NS_RUST_PRIMITIVE::i32;
|
||||
using i64 = NS_RUST_PRIMITIVE::i64;
|
||||
using u8 = NS_RUST_PRIMITIVE::u8;
|
||||
using u16 = NS_RUST_PRIMITIVE::u16;
|
||||
using u32 = NS_RUST_PRIMITIVE::u32;
|
||||
using u64 = NS_RUST_PRIMITIVE::u64;
|
||||
|
||||
using isize = NS_RUST_PRIMITIVE::isize;
|
||||
using usize = NS_RUST_PRIMITIVE::usize;
|
||||
|
||||
using f32 = NS_RUST_PRIMITIVE::f32;
|
||||
using f64 = NS_RUST_PRIMITIVE::f64;
|
||||
|
||||
using str = NS_RUST_PRIMITIVE::str;
|
||||
|
||||
#undef NS_RUST_PRIMITIVE
|
||||
|
||||
// Other types
|
||||
using String = std::u8string;
|
||||
template<typename T>
|
||||
using Vec = std::vector<T>;
|
||||
|
||||
// Expose Result and Option
|
||||
using namespace ::yycc::rust::option;
|
||||
using namespace ::yycc::rust::result;
|
||||
|
||||
// Panic are introduced by including header file
|
||||
// so we do not need re-expose it.
|
||||
|
||||
} // namespace yycc::prelude::rust
|
||||
|
||||
// Expose all members
|
||||
using namespace ::yycc::rust::prelude;
|
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
|
||||
namespace yycc::rust::primitive {
|
||||
|
||||
// `bool` is keyword so should not declare it anymore.
|
||||
// `char` is keyword so should not declare it anymore.
|
||||
|
||||
using i8 = std::int8_t;
|
||||
using i16 = std::int16_t;
|
||||
using i32 = std::int32_t;
|
||||
using i64 = std::int64_t;
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
using isize = std::ptrdiff_t;
|
||||
using usize = std::size_t;
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
|
||||
using str = std::u8string_view;
|
||||
}
|
||||
|
9
src/yycc/string.hpp
Normal file
9
src/yycc/string.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
// TODO:
|
||||
// Add content safe, Rust-like string container "String"
|
||||
// and string view "str" in there.
|
||||
// Once we add it, all string process function can be migrated as their class member,
|
||||
// or keep them independendly but change the type of parameter into our string types.
|
||||
|
||||
namespace yycc::string {}
|
@ -181,12 +181,12 @@ namespace yycc::string::op {
|
||||
std::u8string_view next_str;
|
||||
|
||||
public:
|
||||
CodePointIterator() : CodePointIterator(std::u8string_view()) {}
|
||||
CodePointIterator(const std::u8string_view& strl) : current_str(), next_str(strl) { ++(*this); }
|
||||
YYCC_DEFAULT_COPY_MOVE(CodePointIterator)
|
||||
|
||||
reference operator*() const { return this->current_str; }
|
||||
|
||||
pointer operator->() const { return &this->current_str; }
|
||||
|
||||
CodePointIterator& operator++() {
|
||||
// move next string to current string and analyse it
|
||||
current_str = next_str;
|
||||
@ -214,17 +214,14 @@ namespace yycc::string::op {
|
||||
// return self
|
||||
return *this;
|
||||
}
|
||||
|
||||
CodePointIterator operator++(int) {
|
||||
CodePointIterator temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
bool operator==(const CodePointIterator& other) const {
|
||||
return this->current_str == other.current_str && this->next_str == other.next_str;
|
||||
}
|
||||
|
||||
bool operator!=(const CodePointIterator& other) const { return !(*this == other); }
|
||||
|
||||
private:
|
||||
@ -263,9 +260,9 @@ namespace yycc::string::op {
|
||||
|
||||
public:
|
||||
explicit CodePoint(std::u8string_view u8str) : u8str(u8str) {}
|
||||
YYCC_DEFAULT_COPY_MOVE(CodePoint)
|
||||
|
||||
CodePointIterator begin() const { return CodePointIterator(u8str); }
|
||||
|
||||
CodePointIterator end() const {
|
||||
// Pass empty string view indicate end.
|
||||
return CodePointIterator(std::u8string_view());
|
||||
@ -295,7 +292,7 @@ namespace yycc::string::op {
|
||||
// Do not accept root element always (no empty string).
|
||||
root->is_end = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Insert new words in trie tree.
|
||||
* @details
|
||||
@ -352,18 +349,18 @@ namespace yycc::string::op {
|
||||
|
||||
// YYC MARK:
|
||||
// There is a fatal bug for Trie Tree, but it doesn't matter with our usage scenario.
|
||||
//
|
||||
//
|
||||
// Assume there is two string "ab" and "abcd". If user give "abc",
|
||||
// we should match it with "ab" prefix, but this function will return there is no match.
|
||||
// However, this is impossible for UTF8 sequence.
|
||||
// There is no possibility that two UTF8 sequence, indicating two different Unicode code point respectively,
|
||||
// has the same prefix and different length. Because their first byte must be different,
|
||||
// the first byte indicate the length of sequence.
|
||||
//
|
||||
// This result also can be proven for suffix,
|
||||
//
|
||||
// This result also can be proven for suffix,
|
||||
// because first byte must not be equal to any other continuation bytes.
|
||||
// It is impossible that they have same "ab".
|
||||
//
|
||||
//
|
||||
// So it is safe for our usage scenario although this bug is presented.
|
||||
|
||||
// check whether current is valid end.
|
||||
@ -379,7 +376,7 @@ namespace yycc::string::op {
|
||||
#pragma endregion
|
||||
|
||||
template<bool bDoLeft, bool bDoRight>
|
||||
std::u8string_view internal_strip(const std::u8string_view& strl, const std::u8string_view& words) {
|
||||
static std::u8string_view internal_strip(const std::u8string_view& strl, const std::u8string_view& words) {
|
||||
std::optional<TrieTree> prefix, suffix;
|
||||
if constexpr (bDoLeft) prefix = TrieTree();
|
||||
if constexpr (bDoRight) suffix = TrieTree();
|
||||
@ -419,6 +416,60 @@ namespace yycc::string::op {
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Trim
|
||||
|
||||
template<bool bDoLeft, bool bDoRight>
|
||||
std::u8string_view internal_trim(const std::u8string_view& strl, const std::u8string_view& words) {
|
||||
// check words
|
||||
if (!std::ranges::none_of(words, [](auto c) { return static_cast<uint8_t>(c) & 0x80; })) {
|
||||
throw std::invalid_argument("given words are not all ASCII (<= 0x7F) only");
|
||||
}
|
||||
|
||||
// prepare return value
|
||||
std::u8string_view rv = strl;
|
||||
|
||||
// remove left first
|
||||
if constexpr (bDoLeft) {
|
||||
auto finder = rv.find_first_not_of(words);
|
||||
if (finder == std::u8string_view::npos) {
|
||||
// all string are in given words
|
||||
rv = std::u8string_view();
|
||||
} else {
|
||||
// remove by offset
|
||||
rv = rv.substr(finder);
|
||||
}
|
||||
}
|
||||
|
||||
// remove right
|
||||
if constexpr (bDoRight) {
|
||||
auto finder = rv.find_last_not_of(words);
|
||||
if (finder == std::u8string_view::npos) {
|
||||
// all string are in given words
|
||||
rv = std::u8string_view();
|
||||
} else {
|
||||
// remove by offset
|
||||
rv = rv.substr(0, finder + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// return value
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::u8string_view trim(const std::u8string_view& strl, const std::u8string_view& words) {
|
||||
return internal_trim<true, true>(strl, words);
|
||||
}
|
||||
|
||||
std::u8string_view ltrim(const std::u8string_view& strl, const std::u8string_view& words) {
|
||||
return internal_trim<true, false>(strl, words);
|
||||
}
|
||||
|
||||
std::u8string_view rtrim(const std::u8string_view& strl, const std::u8string_view& words) {
|
||||
return internal_trim<false, true>(strl, words);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Split
|
||||
|
||||
// Reference:
|
||||
@ -426,6 +477,8 @@ namespace yycc::string::op {
|
||||
|
||||
#pragma region Lazy Split Iterator
|
||||
|
||||
LazySplitIterator::LazySplitIterator() : LazySplitIterator(std::nullopt, std::u8string_view()) {}
|
||||
|
||||
LazySplitIterator::LazySplitIterator(std::optional<std::u8string_view> strl, const std::u8string_view& delimiter) :
|
||||
m_current_str(std::nullopt), m_next_str(strl), m_delimiter(delimiter) {
|
||||
// We can archive result by assign string into next string,
|
||||
@ -486,8 +539,8 @@ namespace yycc::string::op {
|
||||
}
|
||||
|
||||
bool LazySplitIterator::operator==(const LazySplitIterator& other) const {
|
||||
return (this->m_current_str == other.m_current_str) && (this->m_next_str == other.m_next_str)
|
||||
&& (this->m_delimiter == other.m_delimiter);
|
||||
// YYC MARK: do not compare the delimiter
|
||||
return (this->m_current_str == other.m_current_str) && (this->m_next_str == other.m_next_str);
|
||||
}
|
||||
|
||||
bool LazySplitIterator::operator!=(const LazySplitIterator& other) const {
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "../macro/printf_checker.hpp"
|
||||
#include "../macro/class_copy_move.hpp"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cstdarg>
|
||||
@ -12,13 +13,19 @@ namespace yycc::string::op {
|
||||
|
||||
#pragma region Printf
|
||||
|
||||
// YYC MARK:
|
||||
// Shitty __attribute__((format(gnu_printf, (A), (B)))) force the type of format string is const char*.
|
||||
// My function signature will cause compile error which can not be removed by any switches.
|
||||
// I guess Clang may have same issue.
|
||||
// So I sadly disable format string check for printf in UTF8 char type.
|
||||
|
||||
/**
|
||||
* @brief Perform an UTF8 string formatting operation.
|
||||
* @param[in] format The format string.
|
||||
* @param[in] ... Argument list of format string.
|
||||
* @return The formatted result.
|
||||
*/
|
||||
std::u8string printf(YYCC_PRINTF_CHECK_FMTSTR const char8_t* format, ...) YYCC_PRINTF_CHECK_ATTR(1, 2);
|
||||
std::u8string printf(/*YYCC_PRINTF_CHECK_FMTSTR*/ const char8_t* format, ...) /*YYCC_PRINTF_CHECK_ATTR(1, 2)*/;
|
||||
/**
|
||||
* @brief Perform an UTF8 string formatting operation.
|
||||
* @param[in] format The format string.
|
||||
@ -138,32 +145,75 @@ namespace yycc::string::op {
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Strip
|
||||
#pragma region Strip and Trim
|
||||
|
||||
/**
|
||||
* @brief Remove leading and trailing whitespace from the string.
|
||||
* @param[in,out] strl The string to be stripped.
|
||||
* @details
|
||||
* This "strip" function is full Unicode supported.
|
||||
* It means that it is different with all other ordinary implementations,
|
||||
* that treat each UTF8 code unit as an invididual chars when stripping.
|
||||
* This function will break given words by UTF8 code point first,
|
||||
* and try to strip these code points in given string.
|
||||
* So it can strip Unicode whitespace or any other characters correctly.
|
||||
* However, obviously, it is slower than ASCII-only version "trim".
|
||||
* If you only need to strip ASCII whitespace (space, tab, newline) or any other code point lower than \c 0x7F,
|
||||
* please consider using trim() for better performance.
|
||||
* @param[in] strl The string to be stripped.
|
||||
* @param[in] words The characters to be stripped.
|
||||
* @return The string view with leading and trailing whitespace removed.
|
||||
* @see See trim() for ASCII-only version "strip".
|
||||
*/
|
||||
std::u8string_view strip(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
|
||||
/**
|
||||
* @brief Remove leading whitespace from the string.
|
||||
* @param[in,out] strl The string to be stripped.
|
||||
* @param[in] strl The string to be stripped.
|
||||
* @param[in] words The characters to be stripped.
|
||||
* @return The string view with leading whitespace removed.
|
||||
* @see See strip() for more info.
|
||||
*/
|
||||
std::u8string_view lstrip(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
|
||||
/**
|
||||
* @brief Remove trailing whitespace from the string.
|
||||
* @param[in,out] strl The string to be stripped.
|
||||
* @param[in] strl The string to be stripped.
|
||||
* @param[in] words The characters to be stripped.
|
||||
* @return The string view with trailing whitespace removed.
|
||||
* @see See strip() for more info.
|
||||
*/
|
||||
std::u8string_view rstrip(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
|
||||
/**
|
||||
* @brief Remove leading and trailing whitespace from the string.
|
||||
* @details
|
||||
* This function is limited "trim" function.
|
||||
* It brutely think each code unit in given words are invididual chars during stripping.
|
||||
* So it can only trim ASCII whitespace (space, tab, newline) or any other code point lower than \c 0x7F.
|
||||
* If you need to trim Unicode whitespace or any other characters,
|
||||
* please consider using strip() for correct behavior.
|
||||
* @param[in] strl The view of string to be trimmed.
|
||||
* @param[in] words The characters to be trimmed.
|
||||
* @return The string view with leading and trailing whitespace removed.
|
||||
* @see See strip() for full Unicode supported version "trim".
|
||||
*/
|
||||
std::u8string_view trim(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
/**
|
||||
* @brief Remove leading whitespace from the string.
|
||||
* @param[in] strl The view of string to be trimmed.
|
||||
* @param[in] words The characters to be trimmed.
|
||||
* @return The string view with leading whitespace removed.
|
||||
* @see See trim() for more info.
|
||||
*/
|
||||
std::u8string_view ltrim(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
/**
|
||||
* @brief Remove trailing whitespace from the string.
|
||||
* @param[in] strl The view of string to be trimmed.
|
||||
* @param[in] words The characters to be trimmed.
|
||||
* @return The string view with trailing whitespace removed.
|
||||
* @see See trim() for more info.
|
||||
*/
|
||||
std::u8string_view rtrim(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Split
|
||||
@ -194,7 +244,9 @@ namespace yycc::string::op {
|
||||
std::u8string_view m_delimiter; ///< Delimiter
|
||||
|
||||
public:
|
||||
LazySplitIterator();
|
||||
LazySplitIterator(std::optional<std::u8string_view> strl, const std::u8string_view& delimiter);
|
||||
YYCC_DEFAULT_COPY_MOVE(LazySplitIterator)
|
||||
|
||||
reference operator*() const;
|
||||
pointer operator->() const;
|
||||
@ -214,6 +266,8 @@ namespace yycc::string::op {
|
||||
|
||||
public:
|
||||
LazySplit(const std::u8string_view& strl, const std::u8string_view& delimiter);
|
||||
YYCC_DEFAULT_COPY_MOVE(LazySplit)
|
||||
|
||||
LazySplitIterator begin() const;
|
||||
LazySplitIterator end() const;
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Create executable testbench
|
||||
add_executable(YYCCTestbench "")
|
||||
# Setup testbench sources
|
||||
target_sources(YYCCTestbench
|
||||
# Create executable test
|
||||
add_executable(YYCCTest "")
|
||||
# Setup test sources
|
||||
target_sources(YYCCTest
|
||||
PRIVATE
|
||||
main.cpp
|
||||
|
||||
@ -20,7 +20,7 @@ PRIVATE
|
||||
yycc/patch/fopen.cpp
|
||||
yycc/patch/stream.cpp
|
||||
yycc/patch/format.cpp
|
||||
yycc/rust/env.cpp
|
||||
yycc/env.cpp
|
||||
yycc/string/reinterpret.cpp
|
||||
yycc/string/op.cpp
|
||||
yycc/num/parse.cpp
|
||||
@ -41,20 +41,21 @@ PRIVATE
|
||||
yycc/carton/wcwidth.cpp
|
||||
yycc/carton/tabulate.cpp
|
||||
yycc/carton/clap.cpp
|
||||
yycc/carton/fft.cpp
|
||||
)
|
||||
target_sources(YYCCTestbench
|
||||
target_sources(YYCCTest
|
||||
PRIVATE
|
||||
FILE_SET HEADERS
|
||||
FILES
|
||||
shared/literals.hpp
|
||||
)
|
||||
# Setup headers
|
||||
target_include_directories(YYCCTestbench
|
||||
target_include_directories(YYCCTest
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
||||
# Setup libraries
|
||||
target_link_libraries(YYCCTestbench
|
||||
target_link_libraries(YYCCTest
|
||||
PRIVATE
|
||||
YYCCommonplace
|
||||
GTest::gtest_main
|
||||
@ -62,4 +63,4 @@ PRIVATE
|
||||
|
||||
# Discover all test
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(YYCCTestbench)
|
||||
gtest_discover_tests(YYCCTest)
|
116
test/yycc/carton/fft.cpp
Normal file
116
test/yycc/carton/fft.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/fft.hpp>
|
||||
#include <initializer_list>
|
||||
|
||||
#define FFT ::yycc::carton::fft
|
||||
|
||||
namespace yycctest::carton::fft {
|
||||
|
||||
using TIndex = size_t;
|
||||
using TFloat = float;
|
||||
using TComplex = std::complex<TFloat>;
|
||||
template<size_t N>
|
||||
using TFft = FFT::Fft<TIndex, TFloat, N>;
|
||||
|
||||
// YYC MARK:
|
||||
// It seems that default epsilon can not fulfill our test (too small).
|
||||
constexpr TFloat TOLERANCE = static_cast<TFloat>(0.0003);
|
||||
//constexpr TFloat tolerance = std::numeric_limits<TFloat>::epsilon();
|
||||
|
||||
template<size_t N>
|
||||
static void test_fft(const std::vector<TFloat>& real_src, const std::vector<TComplex>& dst) {
|
||||
// check given data size
|
||||
ASSERT_EQ(real_src.size(), N);
|
||||
ASSERT_EQ(dst.size(), N);
|
||||
|
||||
// convert real-number source into complex-number source
|
||||
std::vector<TComplex> src(real_src.size());
|
||||
std::generate(src.begin(), src.end(), [data = real_src.begin()]() mutable -> TComplex { return TComplex(*data++); });
|
||||
|
||||
// create FFT instance and compute data
|
||||
TFft<N> fft;
|
||||
fft.compute(src.data());
|
||||
|
||||
// check result with tolerance
|
||||
for (TIndex i = 0u; i < src.size(); ++i) {
|
||||
EXPECT_NEAR(src[i].real(), dst[i].real(), TOLERANCE);
|
||||
EXPECT_NEAR(src[i].imag(), dst[i].imag(), TOLERANCE);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CartonFft, Test1) {
|
||||
std::vector<TFloat> src = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
|
||||
std::vector<TComplex> expected = {{+3.6000e+01f, +0.0000e+00f},
|
||||
{-4.0000e+00f, +9.6569e+00f},
|
||||
{-4.0000e+00f, +4.0000e+00f},
|
||||
{-4.0000e+00f, +1.6569e+00f},
|
||||
{-4.0000e+00f, +0.0000e+00f},
|
||||
{-4.0000e+00f, -1.6569e+00f},
|
||||
{-4.0000e+00f, -4.0000e+00f},
|
||||
{-4.0000e+00f, -9.6569e+00f}};
|
||||
test_fft<8>(src, expected);
|
||||
}
|
||||
|
||||
TEST(CartonFft, Test2) {
|
||||
std::vector<TFloat> src = {6.0f, 1.0f, 7.0f, 2.0f, 7.0f, 4.0f, 8.0f, 7.0f};
|
||||
std::vector<TComplex> expected = {{+4.2000e+01f, +0.0000e+00f},
|
||||
{+4.1421e-01f, +6.6569e+00f},
|
||||
{-2.0000e+00f, +4.0000e+00f},
|
||||
{-2.4142e+00f, +4.6569e+00f},
|
||||
{+1.4000e+01f, +0.0000e+00f},
|
||||
{-2.4142e+00f, -4.6569e+00f},
|
||||
{-2.0000e+00f, -4.0000e+00f},
|
||||
{+4.1421e-01f, -6.6569e+00f}};
|
||||
test_fft<8>(src, expected);
|
||||
}
|
||||
|
||||
TEST(CartonFft, Test3) {
|
||||
std::vector<TFloat> src = {1.0f, 2.0f, 3.0f, 4.0f};
|
||||
std::vector<TComplex> expected = {{+1.0000e+01f, +0.0000e+00f},
|
||||
{-2.0000e+00f, +2.0000e+00f},
|
||||
{-2.0000e+00f, +0.0000e+00f},
|
||||
{-2.0000e+00f, -2.0000e+00f}};
|
||||
test_fft<4>(src, expected);
|
||||
}
|
||||
|
||||
TEST(CartonFft, Test4) {
|
||||
std::vector<TFloat> src = {6.0f, 1.0f, 7.0f, 2.0f};
|
||||
std::vector<TComplex> expected = {{+1.6000e+01f, +0.0000e+00f},
|
||||
{-1.0000e+00f, +1.0000e+00f},
|
||||
{+1.0000e+01f, +0.0000e+00f},
|
||||
{-1.0000e+00f, -1.0000e+00f}};
|
||||
test_fft<4>(src, expected);
|
||||
}
|
||||
|
||||
TEST(CartonFft, Test5) {
|
||||
std::vector<TFloat> src = {4.0f, 4.0f, 4.0f, 4.0f};
|
||||
std::vector<TComplex> expected = {{+1.6000e+01f, +0.0000e+00f},
|
||||
{+0.0000e+00f, +0.0000e+00f},
|
||||
{+0.0000e+00f, +0.0000e+00f},
|
||||
{+0.0000e+00f, +0.0000e+00f}};
|
||||
test_fft<4>(src, expected);
|
||||
}
|
||||
|
||||
TEST(CartonFft, Test6) {
|
||||
std::vector<TFloat> src = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f};
|
||||
std::vector<TComplex> expected = {{+1.3600e+02f, +0.0000e+00f},
|
||||
{-8.0000e+00f, +4.0219e+01f},
|
||||
{-8.0000e+00f, +1.9314e+01f},
|
||||
{-8.0000e+00f, +1.1973e+01f},
|
||||
{-8.0000e+00f, +8.0000e+00f},
|
||||
{-8.0000e+00f, +5.3454e+00f},
|
||||
{-8.0000e+00f, +3.3137e+00f},
|
||||
{-8.0000e+00f, +1.5913e+00f},
|
||||
{-8.0000e+00f, +0.0000e+00f},
|
||||
{-8.0000e+00f, -1.5913e+00f},
|
||||
{-8.0000e+00f, -3.3137e+00f},
|
||||
{-8.0000e+00f, -5.3454e+00f},
|
||||
{-8.0000e+00f, -8.0000e+00f},
|
||||
{-8.0000e+00f, -1.1973e+01f},
|
||||
{-8.0000e+00f, -1.9314e+01f},
|
||||
{-8.0000e+00f, -4.0219e+01f}};
|
||||
test_fft<16>(src, expected);
|
||||
}
|
||||
|
||||
} // namespace yycctest::carton::fft
|
@ -2,7 +2,7 @@
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/constraint.hpp>
|
||||
|
||||
#include <yycc/rust/prelude.hpp>
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
#define CONSTRAINT ::yycc::constraint::Constraint
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/constraint/builder.hpp>
|
||||
|
||||
#include <yycc/rust/prelude.hpp>
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
#define BUILDER ::yycc::constraint::builder
|
||||
using namespace std::literals::string_view_literals;
|
@ -1,15 +1,17 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/rust/env.hpp>
|
||||
#include <yycc/env.hpp>
|
||||
#include <yycc/macro/os_detector.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
#define ENV ::yycc::rust::env
|
||||
#define ENV ::yycc::env
|
||||
|
||||
namespace yycctest::rust::env {
|
||||
namespace yycctest::env {
|
||||
|
||||
constexpr char8_t VAR_NAME[] = u8"HOMER";
|
||||
constexpr char8_t VAR_VALUE[] = u8"doh";
|
||||
|
||||
TEST(RustEnv, All) {
|
||||
TEST(Env, EnvVar) {
|
||||
// Write a new variable should okey
|
||||
{
|
||||
auto rv = ENV::set_var(VAR_NAME, VAR_VALUE);
|
||||
@ -42,4 +44,19 @@ namespace yycctest::rust::env {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
TEST(Env, CurrentExe) {
|
||||
auto rv = ENV::current_exe();
|
||||
ASSERT_TRUE(rv.has_value());
|
||||
|
||||
std::filesystem::path p(rv.value());
|
||||
auto filename = p.filename().u8string();
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Only Windows has special ext.
|
||||
EXPECT_EQ(filename, u8"YYCCTest.exe");
|
||||
#else
|
||||
// Executable in other system are all in plain name.
|
||||
EXPECT_EQ(filename, u8"YYCCTest");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace yycctest::env
|
@ -3,7 +3,7 @@
|
||||
#include <yycc/flag_enum.hpp>
|
||||
#include <cinttypes>
|
||||
|
||||
#include <yycc/rust/prelude.hpp>
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
#define FLAG_ENUM ::yycc::flag_enum
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/num/op.hpp>
|
||||
|
||||
#include <yycc/rust/prelude.hpp>
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
#define OP ::yycc::num::op
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/num/parse.hpp>
|
||||
|
||||
#include <yycc/rust/prelude.hpp>
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
#define PARSE ::yycc::num::parse
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <yycc/num/safe_cast.hpp>
|
||||
#include <yycc/macro/ptr_size_detector.hpp>
|
||||
|
||||
#include <yycc/rust/prelude.hpp>
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
#define CAST ::yycc::num::safe_cast
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
#include <yycc/rust/prelude.hpp>
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
#define OP ::yycc::num::safe_op
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/num/stringify.hpp>
|
||||
|
||||
#include <yycc/rust/prelude.hpp>
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
#define STRINGIFY ::yycc::num::stringify
|
||||
|
@ -11,25 +11,25 @@ namespace yycctest::patch::format {
|
||||
static constexpr std::u8string_view PROBE_STRING_VIEW(PROBE);
|
||||
|
||||
TEST(PatchFormat, OrdinaryFormat) {
|
||||
auto rv = FORMAT::format("{}{}{}{}{}{} world!",
|
||||
auto rv = FORMAT::format("{:c}{}{}{}{}{} world!",
|
||||
PROBE[0],
|
||||
PROBE_STRING.data(),
|
||||
PROBE_STRING.c_str(),
|
||||
PROBE,
|
||||
PROBE_STRING,
|
||||
PROBE_STRING_VIEW);
|
||||
EXPECT_EQ(rv, "104hellohellohellohellohello world!");
|
||||
EXPECT_EQ(rv, "hhellohellohellohellohello world!");
|
||||
}
|
||||
|
||||
TEST(PatchFormat, Utf8Format) {
|
||||
auto rv = FORMAT::format(u8"{}{}{}{}{}{} world!",
|
||||
auto rv = FORMAT::format(u8"{:c}{}{}{}{}{} world!",
|
||||
PROBE[0],
|
||||
PROBE_STRING.data(),
|
||||
PROBE_STRING.c_str(),
|
||||
PROBE,
|
||||
PROBE_STRING,
|
||||
PROBE_STRING_VIEW);
|
||||
EXPECT_EQ(rv, u8"104hellohellohellohellohello world!");
|
||||
EXPECT_EQ(rv, u8"hhellohellohellohellohello world!");
|
||||
}
|
||||
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
|
||||
#include <yycc/rust/prelude.hpp>
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
#define OP ::yycc::string::op
|
||||
using namespace std::literals::string_view_literals;
|
||||
@ -86,6 +86,12 @@ namespace yycctest::string::op {
|
||||
EXPECT_EQ(rv, u8" \taaa");
|
||||
}
|
||||
|
||||
// Full strip
|
||||
{
|
||||
auto rv = OP::strip(u8" ", u8" ");
|
||||
EXPECT_TRUE(rv.empty());
|
||||
}
|
||||
|
||||
// Special strip
|
||||
{
|
||||
auto rv = OP::strip(u8"啊啊啊aaaあああ", u8"啊あ");
|
||||
@ -110,6 +116,32 @@ namespace yycctest::string::op {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StringOp, Trim) {
|
||||
// Normal trim
|
||||
{
|
||||
auto rv = OP::trim(u8" \taaa\n", u8" \t\r\n");
|
||||
EXPECT_EQ(rv, u8"aaa");
|
||||
}
|
||||
{
|
||||
auto rv = OP::ltrim(u8" \taaa\n", u8" \t\r\n");
|
||||
EXPECT_EQ(rv, u8"aaa\n");
|
||||
}
|
||||
{
|
||||
auto rv = OP::rtrim(u8" \taaa\n", u8" \t\r\n");
|
||||
EXPECT_EQ(rv, u8" \taaa");
|
||||
}
|
||||
|
||||
// Bad words
|
||||
{
|
||||
EXPECT_ANY_THROW(OP::trim(u8"q啊啊啊aaaあああp", u8"p啊q"));
|
||||
}
|
||||
|
||||
// Full trim
|
||||
{
|
||||
auto rv = OP::trim(u8" ", u8" ");
|
||||
EXPECT_TRUE(rv.empty());
|
||||
}
|
||||
}
|
||||
TEST(StringOp, Split) {
|
||||
// Normal
|
||||
{
|
@ -3,7 +3,7 @@
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/reinterpret.hpp>
|
||||
|
||||
#include <yycc/rust/prelude.hpp>
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
#define REINTERPRET ::yycc::string::reinterpret
|
||||
#define AS_UINT8(p) static_cast<u8>(p)
|
Reference in New Issue
Block a user