diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5677b97..fb00c43 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,7 +11,7 @@ on: branches: [ "**" ] jobs: - build-gcc: + gcc: runs-on: ubuntu-latest container: those90/ci-image:1.0 env: @@ -22,18 +22,15 @@ jobs: - name: build run: | conan install . -of ./build_gcc --build missing - cmake -S . -B ./build_gcc -DCMAKE_TOOLCHAIN_FILE=./build_gcc/conan_toolchain.cmake - ls -la ./gtest - ls -la ./gtest/test_files - cat ./gtest/test_paths.hpp - echo "****" - pwd - echo "****" + cmake -S . -B ./build_gcc -DCMAKE_TOOLCHAIN_FILE=./build_gcc/conan_toolchain.cmake -DCMAKE_CXX_COMPILER=g++ -DCMAKE_C_COMPILER=gcc cmake --build ./build_gcc -j12 - name: unittests run: ./build_gcc/bin/unittests + - name: example + run: ./build_gcc/bin/example ./examples - build-clang: + + Clang: runs-on: ubuntu-latest container: those90/ci-image:1.0 env: @@ -45,10 +42,38 @@ jobs: run: | conan install . -of ./build_clang --build missing -pr clang cmake -S . -B ./build_clang -DCMAKE_TOOLCHAIN_FILE=./build_clang/conan_toolchain.cmake -DCMAKE_CXX_COMPILER=clang++-17 -DCMAKE_C_COMPILER=clang-17 - ls -la ./gtest - ls -la ./gtest/test_files - cat ./gtest/test_paths.hpp cmake --build ./build_clang -j12 - name: unittests run: ./build_clang/bin/unittests + - name: examples + run: ./build_clang/bin/example ./examples + MSVC: + runs-on: windows-latest + steps: + - name: checkout repo + uses: actions/checkout@v2 + - name: set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.12' + - name: install pip tools + run: | + python -m pip install --upgrade pip + pip install cmake conan + - name: conan + run: | + conan --version + conan profile detect + powershell -Command "(gc C:\Users\runneradmin\.conan2\profiles\default) -replace 'compiler.cppstd=14', 'compiler.cppstd=20' | Out-File -encoding ASCII C:\Users\runneradmin\.conan2\profiles\default" + powershell -Command "(gc C:\Users\runneradmin\.conan2\profiles\default) -replace 'compiler.runtime=dynamic', 'compiler.runtime=static' | Out-File -encoding ASCII C:\Users\runneradmin\.conan2\profiles\default" + conan profile show + - name: build + run: | + conan install . -of build --build missing + cmake -S . -B ./build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=".\build\conan_toolchain.cmake" -DMSVC_STATIC=ON + cmake --build ./build --config Release -j12 + - name: unittests + run: .\build\bin\Release\unittests.exe + - name: example + run: .\build\bin\Release\example.exe ./examples diff --git a/CHANGELOG.md b/CHANGELOG.md index 4844fc6..5e869a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,6 @@ - Define custom types with `CUSTOM_PARAMETER(function-name, "{your type}", "regex pattern", "description") { your callback implementation }` - In your callback implement how to consume capture groups - Access capture groups with `CUKE_PARAM_ARG(index)`, where index starts at 1 and goes from left to write -- Description is an argument in `CUKE_PARAMETER` ([57](https://github.com/ThoSe1990/cwt-cucumber/pull/57)) - Renamed example target `box` to `example` ([56](https://github.com/ThoSe1990/cwt-cucumber/pull/56)) ## [2.3.1] 2024-10-23 diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c1d2b2..bef5c56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,14 @@ if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() +option(MSVC_STATIC "Use static runtime libraries with MSVC" OFF) + +if(MSVC_STATIC) + message(STATUS "MSVC: Setting MT/MTd") + cmake_policy(SET CMP0091 NEW) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif() + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) diff --git a/README.md b/README.md index 6841337..136c01c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -[![CI](https://github.com/ThoSe1990/cucumber-cpp/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/ThoSe1990/cucumber-cpp/actions/workflows/main.yml) [![Build Status](https://dev.azure.com/thomassedlmair/cwt-cucumber/_apis/build/status%2FThoSe1990.cwt-cucumber?branchName=main)](https://dev.azure.com/thomassedlmair/cwt-cucumber/_build/latest?definitionId=14&branchName=main) - +[![CI](https://github.com/ThoSe1990/cucumber-cpp/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/ThoSe1990/cucumber-cpp/actions/workflows/main.yml) ## Coding With Thomas Cucumber: A C++ Cucumber Interpreter @@ -566,18 +565,17 @@ If you want to execute all feature files in a directory (and subdirectory), just CWT-Cucumber supports custom parameter types. This means that we can define custom expressions in our steps to introduce custom types (so to speak) and make the step definition more understandable. -In general: A custom parameter type is an individually defined type that we can use in the step definition. So we give the parameter a function name (as in a step), give the custom type a meaningful name, a description and a regex pattern. Then we implement a callback to consume the capture groups from the regex pattern. Here we use `CUSTOM_PARAMETER(function-name, "{here goes your type}", "regex pattern", "optional: description")`. +In general: A custom parameter type is an individually defined type that we can use in the step definition. So we give the parameter a function name (as in a step), give the custom type a meaningful name, a description and a regex pattern. Then we implement a callback to consume the capture groups from the regex pattern. Here we use `CUSTOM_PARAMETER(function-name, "{here goes your type}", "regex pattern", "description")`. - Function-name: A defined function name (same as in steps) - Custom-Type: Define the type you want, **with curly braces** as string - Regex-Pattern: The regex pattern to match the step, you can use raw string literals, which makes it easier to write regex pattern (see below) -- **Optional** description: A string value to give a meaning full description. This will be printed to the catalog and has no effect on the scenarios. +- Description: A string value to give a meaning full description. This will be printed to the catalog and has no effect on the scenarios. In order to access the capture groups, use `CUKE_PARAM_ARG(index)` where the index starts at 1 from left to right. **Note: You must explicitly return the dedicated type in the callback. The implementation uses type erasure and does not know which type will be used later.** - - + Find all implementations in `examples/step_definition.cpp` and the examples in `examples/features/8_custom_parameters.feature`. ### Example: Pair of Integers @@ -585,7 +583,7 @@ Find all implementations in `examples/step_definition.cpp` and the examples in ` Lets define a type `{pair of integers}`, which will create a `std::pair`: ```cpp -CUSTOM_PARAMETER(custom, "{pair of integers}", R"(var1=(\d+), var2=(\d+))") +CUSTOM_PARAMETER(custom, "{pair of integers}", R"(var1=(\d+), var2=(\d+))", "a pair of integers") { int var1 = CUKE_PARAM_ARG(1); int var2 = CUKE_PARAM_ARG(2); @@ -620,7 +618,7 @@ A more complex example is defined below. We want to parse an event (which is rep `{event}` is fairly easy here: ```cpp -CUSTOM_PARAMETER(custom_event, "{event}", R"('(.*?)')") +CUSTOM_PARAMETER(custom_event, "{event}", R"('(.*?)')", "a custom event") { return CUKE_PARAM_ARG(1).to_string(); } @@ -653,12 +651,12 @@ CUSTOM_PARAMETER( "a custom date pattern") { date begin; - begin.month = std::string(CUKE_PARAM_ARG(1)); + begin.month = CUKE_PARAM_ARG(1).to_string(); begin.day = int(CUKE_PARAM_ARG(2)); begin.year = CUKE_PARAM_ARG(3).as(); date end; - end.month = static_cast(CUKE_PARAM_ARG(4)); + end.month = CUKE_PARAM_ARG(4).to_string(); end.day = static_cast(CUKE_PARAM_ARG(5)); end.year = CUKE_PARAM_ARG(6).as(); @@ -689,6 +687,18 @@ Scenario: Date example Then The beginning month is April and the ending month is Mai ``` +### Strings in Custom Type Parameters + +There are two options in order to create a string value. Some compiler have problems with can not find the correct string type. Therefore we have two options to create a string value from a regex capture: + +Option 1: Dedicated `to_string()` function: +```cpp +CUKE_PARAM_ARG(..).to_string(); +``` +Option 2: Initialize a `std::string` +```cpp +std::string str = CUKE_PARAM_ARG(..) +``` ## Catalog diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index bae18d5..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,28 +0,0 @@ -trigger: -- '*' - -jobs: - - job: WindowsBuild - pool: - vmImage: 'windows-latest' - steps: - - script: | - choco install python3 -y - py.exe -m pip install cmake - py.exe -m pip install conan - displayName: Install CMake, Conan - # for now it does its job ... i know the hardcoded paths are ugly... - - script: | - set PATH=%PATH%;C:\hostedtoolcache\windows\Python\3.12.7\x64\Scripts - conan --version # Now you can use Conan - conan profile detect - powershell -Command "(gc C:\Users\VssAdministrator\.conan2\profiles\default) -replace 'compiler.cppstd=14', 'compiler.cppstd=20' | Out-File -encoding ASCII C:\Users\VssAdministrator\.conan2\profiles\default" - powershell -Command "(gc C:\Users\VssAdministrator\.conan2\profiles\default) -replace 'compiler.runtime=dynamic', 'compiler.runtime=static' | Out-File -encoding ASCII C:\Users\VssAdministrator\.conan2\profiles\default" - displayName: 'Init Conan' - - script: | - set PATH=%PATH%;C:\hostedtoolcache\windows\Python\3.12.7\x64\Scripts - conan install . -of build --build missing - cmake -S . -B ./build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=".\build\conan_toolchain.cmake" - cmake --build ./build --config Release -j6 - .\build\bin\Release\unittests.exe - displayName: 'Win Release Build' diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d20199c..b9f24a2 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -8,7 +8,6 @@ add_executable(${target} install( TARGETS ${target} COMPONENT test - ) target_include_directories(${target} PRIVATE ${PROJECT_SOURCE_DIR}/src) diff --git a/examples/step_definition.cpp b/examples/step_definition.cpp index 95d2bfb..584454a 100644 --- a/examples/step_definition.cpp +++ b/examples/step_definition.cpp @@ -24,21 +24,22 @@ CUSTOM_PARAMETER( "a custom date pattern") { date begin; - begin.month = std::string(CUKE_PARAM_ARG(1)); + begin.month = CUKE_PARAM_ARG(1).to_string(); begin.day = int(CUKE_PARAM_ARG(2)); begin.year = CUKE_PARAM_ARG(3).as(); date end; - end.month = static_cast(CUKE_PARAM_ARG(4)); + end.month = CUKE_PARAM_ARG(4).to_string(); end.day = static_cast(CUKE_PARAM_ARG(5)); end.year = CUKE_PARAM_ARG(6).as(); return date_range{begin, end}; } -CUSTOM_PARAMETER(custom_event, "{event}", R"('(.*?)')") +CUSTOM_PARAMETER(custom_event, "{event}", R"('(.*?)')", "a custom event") { - return CUKE_PARAM_ARG(1).to_string(); + std::string event = CUKE_PARAM_ARG(1); + return event; } WHEN(using_date, "{event} is {date}") diff --git a/src/defines.hpp b/src/defines.hpp index 1fc3488..1ee6b39 100644 --- a/src/defines.hpp +++ b/src/defines.hpp @@ -53,7 +53,6 @@ [[maybe_unused]] cuke::value_array::const_iterator __cuke__values__, \ [[maybe_unused]] std::size_t __cuke__values__count__) -#define GET_MACRO(_1, _2, _3, _4, NAME, ...) NAME /** * @def CUSTOM_PARAMETER(function_name, key, pattern, opt: description) * @brief Creates a custom expression type to use in steps. @@ -67,17 +66,9 @@ * @param description Optional description: This description is only printed as * is in the steps-catalog. */ -#define CUSTOM_PARAMETER(...) \ - GET_MACRO(__VA_ARGS__, _CUSTOM_PARAMETER_WITH_DESC, \ - _CUSTOM_PARAMETER_NO_DESC) \ - (__VA_ARGS__) - -#define _CUSTOM_PARAMETER_WITH_DESC(function_name, key, pattern, description) \ +#define CUSTOM_PARAMETER(function_name, key, pattern, description) \ _CUSTOM_PARAMETER_IMPL(function_name, key, pattern, description) -#define _CUSTOM_PARAMETER_NO_DESC(function_name, key, pattern) \ - _CUSTOM_PARAMETER_IMPL(function_name, key, pattern, "No description found") - /** * @def CUKE_PARAM_ARG(index) * @brief Access capture groups from a custom expression in its callback. diff --git a/src/get_args.hpp b/src/get_args.hpp index a3d355f..afa92f0 100644 --- a/src/get_args.hpp +++ b/src/get_args.hpp @@ -1,7 +1,9 @@ #pragma once +#include #include #include +#include #include "param_info.hpp" #include "value.hpp" @@ -20,8 +22,16 @@ struct conversion template operator T() const { - return cuke::registry().get_expression(key).callback(begin + idx, - values_count); + // NOTE: MSVC treats std::size_t differently then mac/linux + if constexpr (std::is_same_v) + { + return make_parameter_value(begin + idx, values_count); + } + else + { + return cuke::registry().get_expression(key).callback(begin + idx, + values_count); + } } }; diff --git a/src/step.cpp b/src/step.cpp index 72caf63..87c6a29 100644 --- a/src/step.cpp +++ b/src/step.cpp @@ -50,6 +50,8 @@ std::string to_string(step::type type) return "Then"; case step::type::step: return "Step"; + default: + return "..."; } } } // namespace cuke::internal diff --git a/src/util_regex.hpp b/src/util_regex.hpp index b351a70..57f93bb 100644 --- a/src/util_regex.hpp +++ b/src/util_regex.hpp @@ -63,12 +63,12 @@ create_regex_definition(const std::string& step) return {result, type_info}; } -static const std::unordered_set special_chars = { - '.', '^', '$', '*', '+', '?', '[', ']', /* '(', ')', */ '\\', - /* '|' */}; - static std::string add_escape_chars(const std::string& input) { + static const std::unordered_set special_chars = { + '.', '^', '$', '*', '+', '?', '[', ']', /* '(', ')', */ '\\', + /* '|' */}; + std::string result; for (char c : input) { diff --git a/src/value.hpp b/src/value.hpp index 28f598f..a21b6c5 100644 --- a/src/value.hpp +++ b/src/value.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -33,12 +34,13 @@ class value ~value() = default; + operator const std::string&() const { return m_value; } + template operator T() const { return this->as(); } - /** * @brief Checks if the cuke::value is nil * @return True if it is a nil type, else its false @@ -112,7 +114,8 @@ class value * @brief Converts the underlying value to a string. If not possible, this * function throws a std::runtime_error */ - [[nodiscard]] std::string to_string() const { return m_value; } + [[nodiscard]] std::string to_string() { return m_value; } + [[nodiscard]] const std::string& to_string() const { return m_value; } private: std::string m_value;