Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add str_arg_char specializations for ATL/MFC and Qt strings #74

Merged
merged 4 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ codecov:
ignore:
- "deps"
- "include/upa/idna.h"
- "include/upa/url_for_*.h"
- "src/idna.cpp"
- "test"

Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/test-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ jobs:
cxx_standard: 20
cmake_options: ""

- name: g++ C++17 Qt
os: ubuntu-24.04
cxx_compiler: g++
cxx_standard: 17
cmake_options: "-DUPA_TEST_URL_FOR_QT=ON"
install: "qt6-base-dev"

- name: g++ C++17 Codecov
os: ubuntu-latest
cxx_compiler: g++
Expand Down
16 changes: 15 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ option(UPA_INSTALL "Generate the install target." ON)
# library options
option(UPA_AMALGAMATED "Use amalgamated URL library source." OFF)
# tests build options
option(UPA_TEST_URL_FOR_QT "Build tests with Qt strings" OFF)
option(UPA_TEST_COVERAGE "Build tests with code coverage reporting" OFF)
option(UPA_TEST_COVERAGE_CLANG "Build tests with Clang source-based code coverage" OFF)
option(UPA_TEST_SANITIZER "Build tests with Clang sanitizer" OFF)
Expand All @@ -77,6 +78,11 @@ if (UPA_BUILD_FUZZER)
endif()
endif()

# Tests with Qt strings
if (UPA_TEST_URL_FOR_QT)
find_package(Qt6 REQUIRED COMPONENTS Core)
endif()

# Code coverage reporting
if (UPA_TEST_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
# add flags for GCC & LLVM/Clang
Expand Down Expand Up @@ -156,6 +162,7 @@ if (UPA_BUILD_TESTS)
test/test-url.cpp
test/test-url-port.cpp
test/test-url-setters.cpp
test/test-url_for_.cpp
test/test-url_host.cpp
test/test-url_percent_encode.cpp
test/test-url_search_params.cpp
Expand All @@ -169,7 +176,14 @@ if (UPA_BUILD_TESTS)
get_filename_component(test_name ${file} NAME_WE)

add_executable(${test_name} ${file})
target_link_libraries(${test_name} ${upa_lib_target})
target_link_libraries(${test_name} PRIVATE ${upa_lib_target})

if ("${test_name}" STREQUAL "test-url_for_")
if (UPA_TEST_URL_FOR_QT)
target_compile_definitions(${test_name} PRIVATE UPA_TEST_URL_FOR_QT)
target_link_libraries(${test_name} PRIVATE Qt6::Core)
endif()
endif()

add_test(NAME ${test_name}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test
Expand Down
3 changes: 2 additions & 1 deletion Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,8 @@ INPUT = include/upa/url.h \
include/upa/url_result.h \
include/upa/url_search_params.h \
include/upa/url_percent_encode.h \
README.md
README.md \
doc/string_input.md

# This tag can be used to specify the character encoding of the source files
# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Upa URL contains features not specified in the standard:
4. Experimental URLHost class (see proposal: https://github.com/whatwg/url/pull/288): `upa::url_host`
5. The `upa::url_search_params` class has a few additional functions: `remove`, `remove_if`

For string input, the library supports UTF-8, UTF-16, UTF-32 encodings and several string types, including `std::basic_string`, `std::basic_string_view`, null-terminated strings of any char type: `char`, `char8_t`, `char16_t`, `char32_t`, or `wchar_t`.
For string input, the library supports UTF-8, UTF-16, UTF-32 encodings and several string types, including `std::basic_string`, `std::basic_string_view`, null-terminated strings of any char type: `char`, `char8_t`, `char16_t`, `char32_t`, or `wchar_t`. See ["String input"](doc/string_input.md) for more information.

## Installation

Expand Down
23 changes: 23 additions & 0 deletions doc/string_input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## String input

For string input, the library supports UTF-8, UTF-16, UTF-32 encodings and several string types, including `std::basic_string`, `std::basic_string_view`, null-terminated strings of any char type: `char`, `char8_t`, `char16_t`, `char32_t`, or `wchar_t`.

The ATL/MFC and Qt library string types are also supported. To use them you need to include the header files listed in the table below instead of `url.h`:

| Library | Supported string types | Include instead of `url.h` |
|-|-|-|
| ATL/MFC | `CSimpleStringT`, `CStringT`, `CFixedStringT` | `url_for_atl.h` |
| Qt | `QString`, `QStringView`, `QUtf8StringView` | `url_for_qt.h` |

Qt example:
```cpp
#include "upa/url_for_qt.h"
#include <QString>

int main() {
QString str{"http://example.com/"};
upa::url url{str};
std::cout << url.href() << '\n';
return 0;
}
```
63 changes: 63 additions & 0 deletions include/upa/url_for_atl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2024 Rimas Misevičius
// Distributed under the BSD-style license that can be
// found in the LICENSE file.

// This file can be included instead of url.h to allow strings of the
// ATL/MFC classes CSimpleStringT, CStringT, CFixedStringT to be used
// as string input in the functions of this library.
//
#ifndef UPA_URL_FOR_ATL_H
#define UPA_URL_FOR_ATL_H

#include "config.h"
#include "url.h" // IWYU pragma: export
#include <atlsimpstr.h>
#ifdef UPA_CPP_20
# include <concepts>
#else
# include <cstringt.h>
#endif

namespace upa {

template<class StrT>
struct str_arg_char_for_atl {
using type = typename StrT::XCHAR;

static str_arg<type> to_str_arg(const StrT& str) {
return { str.GetString(), static_cast<std::ptrdiff_t>(str.GetLength()) };
}
};

#ifdef UPA_CPP_20

// CStringT and CFixedStringT are derived from CSimpleStringT
template<class StrT>
concept derived_from_CSimpleString =
std::derived_from<StrT, ATL::CSimpleStringT<char, true>> ||
std::derived_from<StrT, ATL::CSimpleStringT<wchar_t, true>> ||
std::derived_from<StrT, ATL::CSimpleStringT<char, false>> ||
std::derived_from<StrT, ATL::CSimpleStringT<wchar_t, false>>;

template<derived_from_CSimpleString StrT>
struct str_arg_char<StrT> : public str_arg_char_for_atl<StrT> {};

#else // UPA_CPP_20

template<typename CharT, bool mfcdll>
struct str_arg_char<ATL::CSimpleStringT<CharT, mfcdll>> :
public str_arg_char_for_atl<ATL::CSimpleStringT<CharT, mfcdll>> {};

template<typename CharT, class StringTraits>
struct str_arg_char<ATL::CStringT<CharT, StringTraits>> :
public str_arg_char_for_atl<ATL::CStringT<CharT, StringTraits>> {};

template<class StrT, int nChars>
struct str_arg_char<ATL::CFixedStringT<StrT, nChars>> :
public str_arg_char_for_atl<ATL::CFixedStringT<StrT, nChars>> {};

#endif // UPA_CPP_20

} // namespace upa

#endif // UPA_URL_FOR_ATL_H
61 changes: 61 additions & 0 deletions include/upa/url_for_qt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2024 Rimas Misevičius
// Distributed under the BSD-style license that can be
// found in the LICENSE file.

// This file can be included instead of url.h to allow strings of the
// Qt classes QString, QStringView, QUtf8StringView to be used as string
// input in the functions of this library.
//
#ifndef UPA_URL_FOR_QT_H
#define UPA_URL_FOR_QT_H

#include "url.h" // IWYU pragma: export
#include <QString>
#if defined(__has_include) && __has_include(<QtVersionChecks>)
# include <QtVersionChecks>
#else
# include <QtGlobal>
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
# include <QStringView>
#endif
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
# include <QUtf8StringView>
#endif
#include <cstddef> // std::ptrdiff_t

namespace upa {

template<class StrT, typename CharT>
struct str_arg_char_for_qt {
static_assert(sizeof(typename StrT::value_type) == sizeof(CharT),
"StrT::value_type and CharT must be the same size");
using type = CharT;

static str_arg<type> to_str_arg(const StrT& str) {
return {
reinterpret_cast<const type*>(str.data()),
static_cast<std::ptrdiff_t>(str.length())
};
}
};

template<>
struct str_arg_char<QString> :
public str_arg_char_for_qt<QString, char16_t> {};

#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
template<>
struct str_arg_char<QStringView> :
public str_arg_char_for_qt<QStringView, char16_t> {};
#endif

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
template<>
struct str_arg_char<QUtf8StringView> :
public str_arg_char_for_qt<QUtf8StringView, char> {};
#endif

} // namespace upa

#endif // UPA_URL_FOR_QT_H
85 changes: 85 additions & 0 deletions test/test-url_for_.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2024 Rimas Misevičius
// Distributed under the BSD-style license that can be
// found in the LICENSE file.
//

#include "doctest-main.h"

#ifdef _MSC_VER
# define UPA_TEST_URL_FOR_ATL
# include "upa/url_for_atl.h"
# include <atlsimpstr.h>
# include <atlstr.h>
# include <cstringt.h>
#endif

#ifdef UPA_TEST_URL_FOR_QT
# include "upa/url_for_qt.h"
# include <QString>
# if defined(__has_include) && __has_include(<QtVersionChecks>)
# include <QtVersionChecks>
# else
# include <QtGlobal>
# endif
# if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
# include <QStringView>
# endif
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
# include <QUtf8StringView>
# endif
#endif


#ifdef UPA_TEST_URL_FOR_ATL

TEST_CASE("ATL::CSimpleStringA input") {
ATL::CStringA basestr;

ATL::CSimpleStringA str_input("http://host/", basestr.GetManager());
upa::url url{ str_input };
CHECK(url.href() == "http://host/");
}

TEST_CASE("ATL::CSimpleStringW input") {
ATL::CStringW basestr;

ATL::CSimpleStringW str_input(L"http://host/", basestr.GetManager());
upa::url url{ str_input };
CHECK(url.href() == "http://host/");
}

TEST_CASE_TEMPLATE_DEFINE("ATL string input", StrT, test_url_for_atl) {
StrT str_input("http://host/");
upa::url url{ str_input };
CHECK(url.href() == "http://host/");
}

TEST_CASE_TEMPLATE_INVOKE(test_url_for_atl,
ATL::CStringA, ATL::CFixedStringT<ATL::CStringA, 16>,
ATL::CStringW, ATL::CFixedStringT<ATL::CStringW, 16>);

#endif // UPA_TEST_URL_FOR_ATL

#ifdef UPA_TEST_URL_FOR_QT

TEST_CASE_TEMPLATE_DEFINE("Qt string input", StrT, test_url_for_qt) {
StrT str_input("http://host/");
upa::url url{ str_input };
CHECK(url.href() == "http://host/");
}

TEST_CASE_TEMPLATE_INVOKE(test_url_for_qt, QString);

#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
TEST_CASE("QStringView input") {
QStringView str_input(u"http://host/");
upa::url url{ str_input };
CHECK(url.href() == "http://host/");
}
#endif

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
TEST_CASE_TEMPLATE_INVOKE(test_url_for_qt, QUtf8StringView);
#endif

#endif // UPA_TEST_URL_FOR_QT