init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
This commit is contained in:
37
cmake/external/glib/CMakeLists.txt
vendored
Normal file
37
cmake/external/glib/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# This file is part of Desktop App Toolkit,
|
||||
# a set of libraries for developing nice desktop applications.
|
||||
#
|
||||
# For license and copyright information please follow this link:
|
||||
# https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
|
||||
add_library(external_glib INTERFACE IMPORTED GLOBAL)
|
||||
add_library(desktop-app::external_glib ALIAS external_glib)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(DESKTOP_APP_GLIB2 REQUIRED IMPORTED_TARGET glib-2.0 gobject-2.0 gio-2.0 gio-unix-2.0)
|
||||
|
||||
target_link_libraries(external_glib
|
||||
INTERFACE
|
||||
PkgConfig::DESKTOP_APP_GLIB2
|
||||
)
|
||||
|
||||
target_compile_definitions(external_glib
|
||||
INTERFACE
|
||||
GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_56
|
||||
GLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_56
|
||||
)
|
||||
|
||||
block()
|
||||
set(BUILD_TESTING OFF)
|
||||
set(BUILD_DOC OFF)
|
||||
set(BUILD_EXAMPLES OFF)
|
||||
add_subdirectory(cppgir EXCLUDE_FROM_ALL)
|
||||
endblock()
|
||||
|
||||
include(generate_cppgir.cmake)
|
||||
|
||||
if (DESKTOP_APP_GLIB2_gio-unix-2.0_VERSION VERSION_GREATER_EQUAL 2.86)
|
||||
generate_cppgir(external_glib GioUnix-2.0)
|
||||
else()
|
||||
generate_cppgir(external_glib Gio-2.0)
|
||||
endif()
|
||||
30
cmake/external/glib/cppgir/.clang-format
vendored
Normal file
30
cmake/external/glib/cppgir/.clang-format
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
Language: Cpp
|
||||
Standard: Cpp11
|
||||
BasedOnStyle: llvm
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
PenaltyBreakBeforeFirstCallParameter: 200
|
||||
IndentWidth: 2
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
#BreakBeforeBraces: Linux
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterStruct: true
|
||||
AfterClass: true
|
||||
AfterUnion: true
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
ColumnLimit: 80
|
||||
ContinuationIndentWidth: 4
|
||||
PointerAlignment: Right
|
||||
AlwaysBreakAfterReturnType: TopLevelDefinitions
|
||||
#BreakInheritanceList: AfterColon
|
||||
FixNamespaceComments: true
|
||||
IndentCaseLabels: true
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
#SpaceBeforeCpp11BracedList: true
|
||||
AlignEscapedNewlines: DontAlign
|
||||
54
cmake/external/glib/cppgir/.gitlab-ci.yml
vendored
Normal file
54
cmake/external/glib/cppgir/.gitlab-ci.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
stages:
|
||||
- build
|
||||
|
||||
default:
|
||||
image: registry.gitlab.com/mnauw/cppgir:noble
|
||||
|
||||
ubuntu-focal-gcc:
|
||||
image: registry.gitlab.com/mnauw/cppgir:focal
|
||||
stage: build
|
||||
script:
|
||||
- mkdir build-gcc
|
||||
- cd build-gcc
|
||||
- cmake ..
|
||||
- make
|
||||
- make examples
|
||||
except:
|
||||
- tags
|
||||
|
||||
ubuntu-focal-clang:
|
||||
image: registry.gitlab.com/mnauw/cppgir:focal
|
||||
stage: build
|
||||
script:
|
||||
- mkdir build-clang
|
||||
- cd build-clang
|
||||
- CC=clang-11 CXX=clang++-11 cmake ..
|
||||
- make
|
||||
- make examples
|
||||
except:
|
||||
- tags
|
||||
|
||||
ubuntu-noble-gcc-13:
|
||||
stage: build
|
||||
script:
|
||||
- mkdir build-gcc-13
|
||||
- cd build-gcc-13
|
||||
- cmake ..
|
||||
- make
|
||||
- make examples
|
||||
except:
|
||||
- tags
|
||||
|
||||
ubuntu-noble-clang-18:
|
||||
stage: build
|
||||
script:
|
||||
- mkdir build-clang-18
|
||||
- cd build-clang-18
|
||||
- CC=clang-18 CXX=clang++-18 cmake ..
|
||||
- make
|
||||
- make examples
|
||||
except:
|
||||
- tags
|
||||
15
cmake/external/glib/cppgir/.gitlab-ci/Dockerfile
vendored
Normal file
15
cmake/external/glib/cppgir/.gitlab-ci/Dockerfile
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM ubuntu:noble
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get -y --no-install-recommends install \
|
||||
build-essential cmake libboost-program-options-dev libboost-fiber-dev \
|
||||
libfmt-dev ronn clang-18 clang-tools-18 \
|
||||
libgirepository1.0-dev libgstreamer1.0-dev \
|
||||
libgtk-3-dev gir1.2-gtk-3.0 \
|
||||
libgtk-4-dev gir1.2-gtk-4.0 \
|
||||
qtbase5-dev meson git
|
||||
RUN apt-get clean
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
9
cmake/external/glib/cppgir/.gitlab-ci/run-docker.sh
vendored
Executable file
9
cmake/external/glib/cppgir/.gitlab-ci/run-docker.sh
vendored
Executable file
@@ -0,0 +1,9 @@
|
||||
set -e
|
||||
|
||||
TAG="registry.gitlab.com/mnauw/cppgir:jammy"
|
||||
|
||||
docker build --build-arg HOST_USER_ID="$UID" --tag "${TAG}" \
|
||||
--file "Dockerfile" .
|
||||
docker run --rm \
|
||||
--volume "$(pwd)/..:/home/user/app" --workdir "/home/user/app" \
|
||||
--tty --interactive "${TAG}" bash
|
||||
3
cmake/external/glib/cppgir/.gitmodules
vendored
Normal file
3
cmake/external/glib/cppgir/.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "expected-lite"]
|
||||
path = expected-lite
|
||||
url = https://github.com/martinmoene/expected-lite.git
|
||||
582
cmake/external/glib/cppgir/CMakeLists.txt
vendored
Normal file
582
cmake/external/glib/cppgir/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,582 @@
|
||||
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
|
||||
if (POLICY CMP0077)
|
||||
cmake_policy(SET CMP0077 NEW)
|
||||
endif ()
|
||||
if (POLICY CMP0069)
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
endif ()
|
||||
if (POLICY CMP0167)
|
||||
# try to use FindBoost for now
|
||||
cmake_policy(SET CMP0167 OLD)
|
||||
endif ()
|
||||
project(cppgir VERSION 2.0.0)
|
||||
|
||||
include(FindPkgConfig)
|
||||
include(GNUInstallDirs)
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
add_compile_options (-Wall -Wextra $<$<COMPILE_LANGUAGE:CXX>:-Wnon-virtual-dtor>)
|
||||
endif()
|
||||
|
||||
# clang debug stdc++
|
||||
if (${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fstandalone-debug")
|
||||
endif()
|
||||
|
||||
## OPTIONS ##
|
||||
option(BUILD_DOC "build documentation" ON)
|
||||
option(BUILD_TOOLS "build generator tool" ON)
|
||||
option(BUILD_EXAMPLES "build examples" ON)
|
||||
option(BUILD_EXAMPLES_MODULES "build module examples" OFF)
|
||||
option(BUILD_EXAMPLES_FORCE "build failing module examples" OFF)
|
||||
set(BUILD_EXAMPLES_OPTIONS "" CACHE STRING "example compile options")
|
||||
option(BUILD_EMBED_IGNORE "embed default ignore" OFF)
|
||||
option(INTERNAL_EXPECTED "use internal expected-lite" ON)
|
||||
option(LINK_STDFS "link to stdc++fs" OFF)
|
||||
set(BUILD_FMT AUTO CACHE STRING "format library")
|
||||
set_property(CACHE BUILD_FMT PROPERTY STRINGS AUTO FMTLIB STDFORMAT)
|
||||
set(GTK_MAJOR GTK3 CACHE STRING "gtk version")
|
||||
set_property(CACHE GTK_MAJOR PROPERTY STRINGS GTK3 GTK4)
|
||||
set(GIR_DIR ${CMAKE_INSTALL_FULL_DATADIR}/gir-1.0 CACHE STRING "extra GIR search directory")
|
||||
# the following will have gir-1.0 appended (for legacy reasons)
|
||||
# best left as-is to follow standard GIR search
|
||||
set(GIR_DEFAULT_DIRS "/usr/local/${CMAKE_INSTALL_DATADIR}:/usr/${CMAKE_INSTALL_DATADIR}"
|
||||
CACHE STRING "fallback GIR search prefix paths (to be suffixed with gir-1.0)")
|
||||
|
||||
## CONTENT ##
|
||||
|
||||
if (BUILD_TOOLS)
|
||||
|
||||
find_package(Boost 1.58 REQUIRED)
|
||||
|
||||
# check fmtlib
|
||||
pkg_check_modules(FORMAT fmt)
|
||||
if (FORMAT_FOUND)
|
||||
set(FORMAT_LIBRARIES "${FORMAT_LDFLAGS}")
|
||||
else ()
|
||||
# fallback for old version without pkg-config
|
||||
find_path(FORMAT_INCLUDE_DIRS fmt/format.h)
|
||||
find_library(FORMAT_LIBRARIES fmt)
|
||||
endif ()
|
||||
if ("${FORMAT_INCLUDE_DIRS}" STREQUAL "FORMAT_INCLUDE_DIRS-NOTFOUND" OR
|
||||
"${FORMAT_LIBRARIES}" STREQUAL "FORMAT_LIBRARIES-NOTFOUND")
|
||||
set(HAVE_FMTLIB OFF)
|
||||
else()
|
||||
set(HAVE_FMTLIB ON)
|
||||
endif()
|
||||
message(STATUS "fmtlib available status: ${HAVE_FMTLIB}")
|
||||
# check C++20 format
|
||||
try_compile(HAVE_STDFORMAT ${CMAKE_BINARY_DIR}
|
||||
SOURCES ${CMAKE_CURRENT_LIST_DIR}/cmake/cpp20_format.cpp
|
||||
CXX_STANDARD 20)
|
||||
message(STATUS "std::format available status: ${HAVE_STDFORMAT}")
|
||||
|
||||
# check and decide on which fmt to use
|
||||
set(USE_FMTLIB NONE)
|
||||
if (BUILD_FMT STREQUAL "AUTO")
|
||||
if (HAVE_FMTLIB)
|
||||
set(USE_FMTLIB ON)
|
||||
elseif (HAVE_STDFORMAT)
|
||||
set(USE_FMTLIB OFF)
|
||||
endif()
|
||||
elseif(BUILD_FMT STREQUAL "FMTLIB" AND HAVE_FMTLIB)
|
||||
set(USE_FMTLIB ON)
|
||||
elseif(BUILD_FMT STREQUAL "STDFORMAT" AND HAVE_STDFORMAT)
|
||||
set(USE_FMTLIB OFF)
|
||||
endif()
|
||||
if (USE_FMTLIB STREQUAL "NONE")
|
||||
message (FATAL_ERROR "no format library found")
|
||||
endif()
|
||||
|
||||
# check C++20 coroutine
|
||||
try_compile(HAVE_CORO ${CMAKE_BINARY_DIR}
|
||||
SOURCES ${CMAKE_CURRENT_LIST_DIR}/cmake/cpp20_coro.cpp
|
||||
CXX_STANDARD 20)
|
||||
message(STATUS "coroutine support available status: ${HAVE_CORO}")
|
||||
|
||||
# required ignore file
|
||||
set(GI_IGNORE_FILE_DIR data)
|
||||
set(GI_IGNORE_FILE cppgir.ignore)
|
||||
file(READ data/cppgir.ignore CPPGIR_IGNORE)
|
||||
if (UNIX)
|
||||
set(GI_IGNORE_FILE_PLATFORM cppgir_unix.ignore)
|
||||
file(READ data/cppgir_unix.ignore CPPGIR_UNIX_IGNORE)
|
||||
set(CPPGIR_WIN_IGNORE "")
|
||||
else ()
|
||||
set(GI_IGNORE_FILE_PLATFORM cppgir_win.ignore)
|
||||
file(READ data/cppgir_win.ignore CPPGIR_WIN_IGNORE)
|
||||
set(CPPGIR_UNIX_IGNORE "")
|
||||
endif ()
|
||||
set(GI_IGNORE_FILE_INSTALL_DIR ${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT_NAME})
|
||||
|
||||
add_executable(cppgir tools/cppgir.cpp
|
||||
tools/genbase.cpp tools/genbase.hpp
|
||||
tools/genns.cpp tools/genns.hpp
|
||||
tools/genutils.cpp tools/genutils.hpp
|
||||
tools/function.cpp tools/function.hpp
|
||||
tools/repository.cpp tools/repository.hpp
|
||||
tools/common.hpp)
|
||||
target_link_libraries(cppgir Boost::boost)
|
||||
if (UNIX)
|
||||
# add fixed fallback search places
|
||||
target_compile_definitions(cppgir PRIVATE
|
||||
-DGI_GIR_DIR=${GIR_DIR}
|
||||
-DGI_DATA_DIR=${CMAKE_INSTALL_FULL_DATADIR}/gir-1.0
|
||||
)
|
||||
if (GIR_DEFAULT_DIRS)
|
||||
target_compile_definitions(cppgir PRIVATE
|
||||
-DDEFAULT_GIRPATH=${GIR_DEFAULT_DIRS}
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
if (BUILD_EMBED_IGNORE)
|
||||
# generate embedded ignore data
|
||||
configure_file(tools/ignore.hpp.in tools/ignore.hpp @ONLY)
|
||||
target_include_directories(cppgir PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/tools)
|
||||
set(GENERATED_IGNORE "")
|
||||
else()
|
||||
target_compile_definitions(cppgir PRIVATE
|
||||
-DDEFAULT_IGNORE_FILE=${GI_IGNORE_FILE_INSTALL_DIR}/${GI_IGNORE_FILE}:${GI_IGNORE_FILE_INSTALL_DIR}/${GI_IGNORE_FILE_PLATFORM})
|
||||
set(GENERATED_IGNORE --ignore ${GI_IGNORE_FILE_DIR}/${GI_IGNORE_FILE}:${GI_IGNORE_FILE_DIR}/${GI_IGNORE_FILE_PLATFORM})
|
||||
endif()
|
||||
if (USE_FMTLIB)
|
||||
target_link_libraries(cppgir ${FORMAT_LIBRARIES})
|
||||
set_property(TARGET cppgir PROPERTY CXX_STANDARD 17)
|
||||
else()
|
||||
set_property(TARGET cppgir PROPERTY CXX_STANDARD 20)
|
||||
endif()
|
||||
if (LINK_STDFS)
|
||||
# some older gcc might sometimes (?) need this, even in c++17 mode
|
||||
# see issue #80
|
||||
target_link_libraries(cppgir stdc++fs)
|
||||
endif ()
|
||||
add_executable(CppGir::cppgir ALIAS cppgir)
|
||||
|
||||
endif () # BUILD_TOOLS
|
||||
|
||||
add_library(gi INTERFACE)
|
||||
target_include_directories(gi INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/override>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/override>"
|
||||
)
|
||||
add_library(CppGir::gi ALIAS gi)
|
||||
|
||||
if (INTERNAL_EXPECTED)
|
||||
set(EXPECTED_LITE_INCLUDE "expected-lite/include")
|
||||
if (EXISTS ${CMAKE_CURRENT_LIST_DIR}/${EXPECTED_LITE_INCLUDE}/nonstd/expected.hpp)
|
||||
target_include_directories(gi INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/${EXPECTED_LITE_INCLUDE}>"
|
||||
)
|
||||
else ()
|
||||
message (FATAL_ERROR "missing submodule expected-lite")
|
||||
endif ()
|
||||
else ()
|
||||
find_package(expected-lite)
|
||||
if (expected-lite_FOUND)
|
||||
target_link_libraries(gi INTERFACE nonstd::expected-lite)
|
||||
else ()
|
||||
target_compile_features(gi INTERFACE cxx_std_23)
|
||||
message (WARNING "thus cppgir headers will require C++23")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
||||
if (BUILD_TOOLS)
|
||||
|
||||
if (BUILD_EXAMPLES OR BUILD_TESTING)
|
||||
pkg_check_modules(GOBJECT gobject-2.0)
|
||||
endif ()
|
||||
if (BUILD_EXAMPLES)
|
||||
pkg_check_modules(GIO gio-2.0 gio-unix-2.0)
|
||||
pkg_check_modules(GST gstreamer-1.0)
|
||||
if (GTK_MAJOR STREQUAL "GTK3")
|
||||
pkg_check_modules(GTK gtk+-3.0)
|
||||
set(GTK_GIR Gtk-3.0)
|
||||
endif ()
|
||||
if (GTK_MAJOR STREQUAL "GTK4")
|
||||
# only plain gio is a dependency, but GIR requires other gio parts
|
||||
pkg_check_modules(GTK gtk4 gtk4-unix-print gio-unix-2.0)
|
||||
set(GTK_GIR Gtk-4.0)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
## TEST ##
|
||||
|
||||
if (BUILD_TESTING AND GOBJECT_FOUND)
|
||||
add_library(ltest
|
||||
test/test_object.c test/test_object.h test/test_boxed.c test/test_boxed.h)
|
||||
target_include_directories(ltest PUBLIC "gi" "override")
|
||||
target_link_libraries(ltest gi ${GOBJECT_LDFLAGS})
|
||||
target_compile_options(ltest PUBLIC ${GOBJECT_CFLAGS})
|
||||
|
||||
add_executable(gi-test test/main.cpp)
|
||||
target_link_libraries(gi-test PRIVATE ltest)
|
||||
add_test(NAME gi-test COMMAND gi-test)
|
||||
|
||||
# a C++17 version of the above
|
||||
add_executable(gi-test-17 test/main.cpp)
|
||||
target_link_libraries(gi-test-17 PRIVATE ltest)
|
||||
set_property(TARGET gi-test-17 PROPERTY CXX_STANDARD 17)
|
||||
add_test(NAME gi-test-17 COMMAND gi-test-17)
|
||||
endif ()
|
||||
|
||||
## EXAMPLES ##
|
||||
|
||||
# generated wrappers' dir
|
||||
set (GENERATED_DIR_DEFAULT "/tmp/gi")
|
||||
set (EXAMPLES_LIBS "")
|
||||
if (NOT GENERATED_DIR)
|
||||
set (GENERATED_DIR ${GENERATED_DIR_DEFAULT})
|
||||
endif ()
|
||||
# arguments to generator
|
||||
if (NOT DEFINED GENERATED_ARGS_DEFAULT)
|
||||
set(GENERATED_ARGS_DEFAULT --class --class-full --call-args 0 --basic-collection)
|
||||
endif ()
|
||||
if (NOT DEFINED GENERATED_ARGS_EXTRA)
|
||||
set(GENERATED_ARGS_EXTRA "")
|
||||
endif ()
|
||||
set (GENERATED_ARGS ${GENERATED_ARGS_DEFAULT} ${GENERATED_ARGS_EXTRA})
|
||||
|
||||
set(EXAMPLE_TARGETS "")
|
||||
set(EXAMPLE_NS "")
|
||||
# if both --dl and --expected are enabled,
|
||||
# all function return value become gi::result<>
|
||||
# to keep examples straight and simple,
|
||||
# only few of them are adjusted to handle that scenario
|
||||
set(PLAIN_API ON)
|
||||
if ("${GENERATED_ARGS}" MATCHES "--dl")
|
||||
set (EXAMPLES_LIBS ${CMAKE_DL_LIBS})
|
||||
if ("${GENERATED_ARGS}" MATCHES "--expected")
|
||||
set(PLAIN_API OFF)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (BUILD_EXAMPLES)
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag("-mxgot" NEEDS_LARGE_GOT)
|
||||
endif ()
|
||||
|
||||
if (GOBJECT_FOUND)
|
||||
add_executable(example-gobject EXCLUDE_FROM_ALL examples/gobject.cpp)
|
||||
target_compile_options(example-gobject PRIVATE ${GOBJECT_CFLAGS})
|
||||
target_link_libraries(example-gobject PRIVATE ${GOBJECT_LDFLAGS})
|
||||
set_property(TARGET example-gobject PROPERTY CXX_STANDARD 14)
|
||||
|
||||
message(STATUS "adding GObject example")
|
||||
set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} example-gobject)
|
||||
set(EXAMPLE_NS ${EXAMPLE_NS} GObject-2.0)
|
||||
endif ()
|
||||
|
||||
if (GIO_FOUND)
|
||||
add_executable(example-gio EXCLUDE_FROM_ALL examples/gio.cpp)
|
||||
target_compile_options(example-gio PRIVATE ${GIO_CFLAGS})
|
||||
target_link_libraries(example-gio PRIVATE ${GIO_LDFLAGS})
|
||||
set_property(TARGET example-gio PROPERTY CXX_STANDARD 17)
|
||||
|
||||
message(STATUS "adding Gio communication example")
|
||||
set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} example-gio)
|
||||
endif ()
|
||||
|
||||
if (GIO_FOUND)
|
||||
add_executable(example-gio-dbus-client EXCLUDE_FROM_ALL examples/gio-dbus-client.cpp)
|
||||
target_compile_options(example-gio-dbus-client PRIVATE ${GIO_CFLAGS})
|
||||
target_link_libraries(example-gio-dbus-client PRIVATE ${GIO_LDFLAGS})
|
||||
|
||||
message(STATUS "adding Gio dbus example")
|
||||
set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} example-gio-dbus-client)
|
||||
set(EXAMPLE_NS ${EXAMPLE_NS} Gio-2.0)
|
||||
endif ()
|
||||
|
||||
if (GIO_FOUND AND PLAIN_API)
|
||||
find_package(Boost 1.65 COMPONENTS fiber)
|
||||
if (Boost_FOUND)
|
||||
# no import target; multiple calls do not override first call targets
|
||||
add_executable(example-gio-async EXCLUDE_FROM_ALL examples/gio-async.cpp)
|
||||
target_include_directories(example-gio-async PRIVATE ${Boost_INCLUDE_DIRS})
|
||||
target_compile_options(example-gio-async PRIVATE ${GIO_CFLAGS})
|
||||
target_link_libraries(example-gio-async PRIVATE ${GIO_LDFLAGS} ${Boost_LIBRARIES})
|
||||
|
||||
message(STATUS "adding Gio async example")
|
||||
set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} example-gio-async)
|
||||
else ()
|
||||
set(GIO_ASYNC_EXAMPLE_TARGET "")
|
||||
message(STATUS "disabling Gio async example")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (GIO_FOUND AND HAVE_CORO AND PLAIN_API)
|
||||
add_executable(example-gio-async-co EXCLUDE_FROM_ALL examples/gio-async-co.cpp)
|
||||
target_compile_options(example-gio-async-co PRIVATE ${GIO_CFLAGS})
|
||||
target_link_libraries(example-gio-async-co PRIVATE ${GIO_LDFLAGS})
|
||||
set_property(TARGET example-gio-async-co PROPERTY CXX_STANDARD 20)
|
||||
|
||||
message(STATUS "adding Gio async coroutine example")
|
||||
set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} example-gio-async-co)
|
||||
set(EXAMPLE_NS ${EXAMPLE_NS} Gio-2.0)
|
||||
endif ()
|
||||
|
||||
if (GST_FOUND AND PLAIN_API)
|
||||
add_executable(example-gst EXCLUDE_FROM_ALL examples/gst.cpp)
|
||||
# add generated files
|
||||
foreach (GENSRC IN ITEMS ${GENERATED_DIR}/glib/glib.cpp
|
||||
${GENERATED_DIR}/gst/gst.cpp ${GENERATED_DIR}/gobject/gobject.cpp)
|
||||
target_sources(example-gst PRIVATE ${GENSRC})
|
||||
set_property(SOURCE ${GENSRC} PROPERTY GENERATED true)
|
||||
endforeach ()
|
||||
target_link_libraries(example-gst PRIVATE ${GST_LDFLAGS})
|
||||
target_compile_options(example-gst PRIVATE ${GST_CFLAGS})
|
||||
# a lot of class methods are wrapped these days, so there is some overlap
|
||||
# however, no class implementation is used here, so arrange for suppression
|
||||
target_compile_definitions(example-gst PRIVATE GI_CLASS_IMPL_PRAGMA=1)
|
||||
set_property(TARGET example-gst PROPERTY CXX_STANDARD 14)
|
||||
if (NEEDS_LARGE_GOT)
|
||||
set_property(SOURCE ${GENERATED_DIR}/gst/gst.cpp APPEND PROPERTY COMPILE_OPTIONS "-mxgot")
|
||||
endif ()
|
||||
|
||||
message(STATUS "adding Gst example")
|
||||
set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} example-gst)
|
||||
set(EXAMPLE_NS ${EXAMPLE_NS} Gst-1.0)
|
||||
|
||||
function(add_gst_module_example TARGET_NAME)
|
||||
add_executable(${TARGET_NAME} EXCLUDE_FROM_ALL examples/gst.cpp)
|
||||
# add generated files
|
||||
foreach (GENSRC IN ITEMS ${GENERATED_DIR}/gobject/gobject.cppm
|
||||
${GENERATED_DIR}/gmodule/gmodule.cppm ${GENERATED_DIR}/gst/gst.cppm)
|
||||
target_sources(${TARGET_NAME} PRIVATE FILE_SET CXX_MODULES
|
||||
BASE_DIRS ${GENERATED_DIR} FILES ${GENSRC})
|
||||
set_property(SOURCE ${GENSRC} PROPERTY GENERATED true)
|
||||
endforeach ()
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE ${GST_LDFLAGS})
|
||||
target_compile_options(${TARGET_NAME} PRIVATE ${GST_CFLAGS})
|
||||
target_compile_definitions(${TARGET_NAME} PRIVATE USE_GI_MODULE=1 GI_CLASS_IMPL_PRAGMA=1)
|
||||
set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD 20)
|
||||
set_property(TARGET ${TARGET_NAME} PROPERTY CXX_SCAN_FOR_MODULES ON)
|
||||
if (NEEDS_LARGE_GOT)
|
||||
set_property(SOURCE ${GENERATED_DIR}/gst/gst.cppm APPEND PROPERTY COMPILE_OPTIONS "-mxgot")
|
||||
endif ()
|
||||
|
||||
message(STATUS "adding Gst module example ${TARGET_NAME}")
|
||||
set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} ${TARGET_NAME} PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
# (separate) module variant, similar to case above
|
||||
# GCC fails to link this properly :-( (missing symbols in object files)
|
||||
if (CMAKE_VERSION GREATER_EQUAL 3.28 AND BUILD_EXAMPLES_MODULES AND
|
||||
(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR BUILD_EXAMPLES_FORCE))
|
||||
add_gst_module_example(example-gst-mod)
|
||||
add_gst_module_example(example-gst-mod-extern)
|
||||
target_compile_definitions(example-gst-mod-extern PRIVATE GI_MODULE_EXTERN=1)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (GTK_FOUND AND PLAIN_API)
|
||||
add_executable(example-gtk EXCLUDE_FROM_ALL examples/gtk.cpp examples/gtk-obj.cpp)
|
||||
target_compile_options(example-gtk PRIVATE ${GTK_CFLAGS})
|
||||
target_link_libraries(example-gtk PRIVATE ${GTK_LIBRARIES})
|
||||
target_compile_options(example-gtk PRIVATE -DEXAMPLES_DIR=${CMAKE_CURRENT_LIST_DIR}/examples)
|
||||
if (NEEDS_LARGE_GOT)
|
||||
set_property(SOURCE examples/gtk-obj.cpp APPEND PROPERTY COMPILE_OPTIONS "-mxgot")
|
||||
endif ()
|
||||
|
||||
# sanity check
|
||||
if (NOT GTK_GIR)
|
||||
message(FATAL_ERROR "unknown Gtk GIR")
|
||||
endif ()
|
||||
message(STATUS "adding Gtk example ${GTK_GIR}")
|
||||
set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} example-gtk)
|
||||
set(EXAMPLE_NS ${EXAMPLE_NS} ${GTK_GIR})
|
||||
|
||||
function(add_gtk_module_example TARGET_NAME)
|
||||
add_executable(${TARGET_NAME} EXCLUDE_FROM_ALL)
|
||||
target_sources(${TARGET_NAME} PRIVATE examples/gtk.cpp
|
||||
PRIVATE FILE_SET CXX_MODULES BASE_DIRS ${GENERATED_DIR}
|
||||
FILES ${GENERATED_DIR}/gtk/gtk_rec.cppm)
|
||||
target_compile_options(${TARGET_NAME} PRIVATE ${GTK_CFLAGS})
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE ${GTK_LIBRARIES})
|
||||
target_compile_options(${TARGET_NAME} PRIVATE -DUSE_GI_MODULE=1
|
||||
-DEXAMPLES_DIR=${CMAKE_CURRENT_LIST_DIR}/examples)
|
||||
set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD 20)
|
||||
set_property(TARGET ${TARGET_NAME} PROPERTY CXX_SCAN_FOR_MODULES ON)
|
||||
set_property(SOURCE ${GENERATED_DIR}/gtk/gtk_rec.cppm PROPERTY GENERATED ON)
|
||||
if (NEEDS_LARGE_GOT)
|
||||
set_property(SOURCE ${GENERATED_DIR}/gtk/gtk_rec.cppm APPEND PROPERTY COMPILE_OPTIONS "-mxgot")
|
||||
endif ()
|
||||
|
||||
message(STATUS "adding Gtk recursive module example ${TARGET_NAME}")
|
||||
set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} ${TARGET_NAME} PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
# (recursive) module variant, similar to non-module case above
|
||||
if (CMAKE_VERSION GREATER_EQUAL 3.28 AND BUILD_EXAMPLES_MODULES)
|
||||
add_gtk_module_example(example-gtk-mod)
|
||||
add_gtk_module_example(example-gtk-mod-extern)
|
||||
target_compile_definitions(example-gtk-mod-extern PRIVATE GI_MODULE_EXTERN=1)
|
||||
endif ()
|
||||
|
||||
endif ()
|
||||
|
||||
# optional Qt example
|
||||
if (BUILD_EXAMPLES)
|
||||
find_package(Qt5Core 5.9)
|
||||
endif ()
|
||||
if (Qt5Core_FOUND AND GIO_FOUND AND PLAIN_API)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
add_executable(example-gio-qt-async EXCLUDE_FROM_ALL examples/gio-qt-async.cpp)
|
||||
target_compile_options(example-gio-qt-async PRIVATE ${GIO_CFLAGS})
|
||||
target_link_libraries(example-gio-qt-async PRIVATE ${GIO_LDFLAGS} Qt5::Core)
|
||||
set_target_properties(example-gio-qt-async PROPERTIES AUTOMOC ON)
|
||||
|
||||
message(STATUS "adding Qt GIO async example")
|
||||
set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} example-gio-qt-async)
|
||||
endif ()
|
||||
|
||||
add_custom_command(OUTPUT ${GENERATED_DIR}
|
||||
COMMENT "Generating wrapper code for: ${EXAMPLE_NS}"
|
||||
DEPENDS cppgir ${GI_IGNORE_FILE_DIR}/${GI_IGNORE_FILE} ${GI_IGNORE_FILE_DIR}/${GI_IGNORE_FILE_PLATFORM}
|
||||
COMMAND cppgir ${GENERATED_ARGS} ${GENERATED_IGNORE}
|
||||
--output ${GENERATED_DIR} ${EXAMPLE_NS}
|
||||
COMMAND cmake -E touch_nocreate ${GENERATED_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# example might have been added if dependencies found
|
||||
# make sure to disable as needed
|
||||
if (NOT BUILD_EXAMPLES)
|
||||
set(EXAMPLE_TARGETS "")
|
||||
endif ()
|
||||
|
||||
message(STATUS "example programs: ${EXAMPLE_TARGETS}")
|
||||
add_custom_target(examples)
|
||||
if (EXAMPLE_TARGETS)
|
||||
add_dependencies(examples ${EXAMPLE_TARGETS})
|
||||
endif ()
|
||||
|
||||
add_custom_target(wrappers DEPENDS ${GENERATED_DIR})
|
||||
foreach (example ${EXAMPLE_TARGETS})
|
||||
target_link_libraries(${example} PRIVATE gi ${EXAMPLES_LIBS})
|
||||
target_include_directories(${example} PRIVATE ${GENERATED_DIR})
|
||||
target_compile_options(${example} PRIVATE ${BUILD_EXAMPLES_OPTIONS})
|
||||
add_dependencies(${example} wrappers)
|
||||
endforeach ()
|
||||
|
||||
|
||||
## INSTALL ##
|
||||
|
||||
# manpage processor
|
||||
find_program(RONN ronn DOC "ronn markdown man page processor")
|
||||
if (${RONN} STREQUAL "RONN-NOTFOUND")
|
||||
message(STATUS "ronn manpage processor not found; not building manpage")
|
||||
elseif (BUILD_DOC)
|
||||
message(STATUS "building manpage")
|
||||
add_custom_command(OUTPUT cppgir.1
|
||||
COMMAND ${RONN} --roff --pipe ${CMAKE_CURRENT_LIST_DIR}/docs/cppgir.md > cppgir.1
|
||||
DEPENDS docs/cppgir.md
|
||||
WORKING_DIRECTORY .)
|
||||
add_custom_target(manpages ALL DEPENDS cppgir.1)
|
||||
endif()
|
||||
|
||||
endif () # BUILD_TOOLS
|
||||
|
||||
# headers
|
||||
install(DIRECTORY gi override
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
||||
if (INTERNAL_EXPECTED)
|
||||
install(DIRECTORY ${EXPECTED_LITE_INCLUDE}/nonstd
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/gi)
|
||||
endif ()
|
||||
|
||||
# doc
|
||||
install(FILES README.md docs/cppgir.md
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||
install(DIRECTORY examples
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR}
|
||||
PATTERN external EXCLUDE)
|
||||
if (TARGET manpages)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cppgir.1
|
||||
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||
endif()
|
||||
|
||||
# pkgconfig
|
||||
set(PKG_CONFIG "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
|
||||
set(PKG_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
# configure pkg config file
|
||||
configure_file("cmake/cppgir.pc.in" "${PKG_CONFIG}" @ONLY)
|
||||
install(FILES "${PKG_CONFIG}"
|
||||
DESTINATION "${PKG_CONFIG_INSTALL_DIR}")
|
||||
|
||||
# cmake EXPORTS
|
||||
set(CONFIG_PACKAGE_LOCATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
|
||||
set(CONFIG_VERSION_NAME ${PROJECT_NAME}-config-version.cmake)
|
||||
set(CONFIG_TARGETS_NAME ${PROJECT_NAME}-targets.cmake)
|
||||
set(CONFIG_NAME ${PROJECT_NAME}-config.cmake)
|
||||
if (INTERNAL_EXPECTED OR NOT expected-lite_FOUND)
|
||||
set(CONFIG_NAME_IN ${CONFIG_NAME})
|
||||
else ()
|
||||
set(CONFIG_NAME_IN ${PROJECT_NAME}-config-deps.cmake)
|
||||
endif ()
|
||||
set(TARGETS_EXPORT_NAME CppGirTargets)
|
||||
|
||||
if (BUILD_TOOLS)
|
||||
|
||||
# generator
|
||||
install(TARGETS cppgir
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
EXPORT "${TARGETS_EXPORT_NAME}")
|
||||
|
||||
# ignore file
|
||||
if (NOT BUILD_EMBED_IGNORE)
|
||||
install(FILES ${GI_IGNORE_FILE_DIR}/${GI_IGNORE_FILE} ${GI_IGNORE_FILE_DIR}/${GI_IGNORE_FILE_PLATFORM}
|
||||
DESTINATION ${GI_IGNORE_FILE_INSTALL_DIR})
|
||||
endif()
|
||||
|
||||
endif () # BUILD_TOOLS
|
||||
|
||||
# headers
|
||||
install(TARGETS gi EXPORT "${TARGETS_EXPORT_NAME}")
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake/${CONFIG_VERSION_NAME}"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
ARCH_INDEPENDENT
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake/${CONFIG_VERSION_NAME}"
|
||||
DESTINATION ${CONFIG_PACKAGE_LOCATION}
|
||||
)
|
||||
|
||||
install(FILES cmake/${CONFIG_NAME_IN} RENAME ${CONFIG_NAME}
|
||||
DESTINATION ${CONFIG_PACKAGE_LOCATION}
|
||||
)
|
||||
|
||||
export(EXPORT ${TARGETS_EXPORT_NAME}
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${CONFIG_TARGETS_NAME}"
|
||||
NAMESPACE CppGir::
|
||||
)
|
||||
|
||||
install(EXPORT ${TARGETS_EXPORT_NAME}
|
||||
FILE ${CONFIG_TARGETS_NAME}
|
||||
NAMESPACE CppGir::
|
||||
DESTINATION ${CONFIG_PACKAGE_LOCATION}
|
||||
)
|
||||
|
||||
# uninstall target;
|
||||
# intermediate directories are not removed though
|
||||
if(NOT TARGET uninstall)
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake-uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
endif()
|
||||
21
cmake/external/glib/cppgir/LICENSE
vendored
Normal file
21
cmake/external/glib/cppgir/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Mark Nauwelaerts
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
221
cmake/external/glib/cppgir/README.md
vendored
Normal file
221
cmake/external/glib/cppgir/README.md
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
# cppgir
|
||||
|
||||
`cppgir` is a [GObject-Introspection](https://wiki.gnome.org/Projects/GObjectIntrospection)
|
||||
C++ binding wrapper generator. That is, it processes `.gir` files derived
|
||||
from GObject-Introspection annotations into a set of C++ files defining
|
||||
suitable namespaces, classes and other types that together from a C++ binding.
|
||||
In this way, the plain C libraries and objects become available as native
|
||||
objects along with (RAII) managed resource handling. The generated code
|
||||
only requires a C++14 compiler and library (as well as obviously the underlying
|
||||
C headers and libraries that are being wrapped).
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
The code generator can be built using [CMake](https://cmake.org) or
|
||||
[meson](https://mesonbuild.com/) and requires
|
||||
Boost and either [fmtlib](http://fmtlib.net/) or a C++20 std library with format
|
||||
support (e.g. gcc 13+). While it obviously depends on distribution,
|
||||
these may be typically be obtained by installing following packages;
|
||||
|
||||
libfmt-dev libboost-dev
|
||||
|
||||
If it is required to compile the manual page ronn is required to be installed
|
||||
|
||||
The following step will provide an additional dependency as a submodule;
|
||||
|
||||
git submodule update --init
|
||||
|
||||
With all that in place, the usual steps apply, so e.g.
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build .
|
||||
cmake --install .
|
||||
|
||||
or alternatively
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
meson setup . ..
|
||||
meson compile
|
||||
meson install
|
||||
|
||||
The installed components consist of:
|
||||
|
||||
* the code generator executable
|
||||
* (if not embedded by build option) default ignore files
|
||||
* common headers required by the generated code and default overrides
|
||||
|
||||
The latter is needed when using the generated code, the former only
|
||||
at generation time.
|
||||
|
||||
In case of CMake build, it is also possible to build some examples whose
|
||||
sources are also installed along with some documentation.
|
||||
As such, the meson system only builds and install the (most) relevant parts,
|
||||
as may be useful when used as a subproject.
|
||||
|
||||
For either build system, an [example](examples/external) shows how `cppgir`
|
||||
can be used in a project.
|
||||
|
||||
## Release numbering and API stability
|
||||
|
||||
The generated code for an even-numbered major version should be (compile) API
|
||||
stable. If the major version turns odd, then a cycle/series of incompatible
|
||||
changes may occur, which after actual practice and time can then become
|
||||
the next even stable version.
|
||||
|
||||
|
||||
## Running
|
||||
|
||||
All details can be found in the [manpage](docs/cppgir.md), but a simple
|
||||
example wrapping [GStreamer](https://gstreamer.freedesktop.org/) libraries is
|
||||
as follows:
|
||||
|
||||
generate --output /tmp/gi GStreamer-1.0
|
||||
|
||||
The above (obviously) assumes that suitable `.gir` files are present in
|
||||
the usual location, as typically installed by a `-dev` package. It is
|
||||
also assumed the generator has been installed (so "running installed").
|
||||
Running the generator uninstalled is also possible, but then (if not embedded) the
|
||||
default ignore files in `data` directory have to explicitly specified as well
|
||||
(using either option or environment variable as specified in manual).
|
||||
|
||||
That's it, all wrapping code is now available in `/tmp/gi` (in a number of
|
||||
subdirectories). It only requires a C++14 compiler and library and can be
|
||||
included and compiled into the target library or executable. The latter then
|
||||
has no additional dependencies other than the plain C libraries (gstreamer and
|
||||
lower level ones in this case).
|
||||
|
||||
|
||||
## Compilation
|
||||
|
||||
The wrapping binding code presents an API along the lines of any other binding,
|
||||
which also means it maps pretty straight onto the original library API.
|
||||
So all API is simply found where expected. To use it fully inline,
|
||||
the following include snippet suffices:
|
||||
|
||||
#define GI_INLINE 1
|
||||
#include <gst/gst.hpp>
|
||||
|
||||
Of course, the include path must have been setup properly to contain the output
|
||||
root of the generated code (i.e. `/tmp/gi` in previous example). It must also
|
||||
contain the proper path for header code supplied by this repo (using e.g. a
|
||||
pkgconfig fragment if it has been installed, or an IMPORT target or by other
|
||||
means).
|
||||
|
||||
See the [manpage](docs/cppgir.md) for additional essential remarks on the API
|
||||
of the generated code or typical non-inline use of the code.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
The examples (in the so-named directory) illustrate and cover various practical
|
||||
aspects and use of the generated API. While they are all simple and trivial,
|
||||
they do illustrate use in various domains such as `Gio`, `Gst` and `Gtk`.
|
||||
|
||||
For example, when it comes to traditional C `libgio`, there is usually a choice
|
||||
between a synchronous (blocking) call and a corresponding asynchronous
|
||||
(non-blocking) call. The latter is then typically used to establish a chain of
|
||||
completion handlers. In other settings and languages, it has become customary
|
||||
to employ async/await operations/keywords. That way, the flow of code remains
|
||||
linear as in the blocking case. However, some form of callback chain is
|
||||
typically present behind the (implementation) scenes and a more practical
|
||||
consequence is that async/await keywords need to be sprinkled in quite some
|
||||
places (down to call stack depth). That, in turn, is then intrusive/disruptive
|
||||
in its own way.
|
||||
|
||||
Alternatively, one can use so-called stackful co-routines or fibers to maintain
|
||||
stack context when execution has to be suspended (e.g. awaiting I/O, or some
|
||||
condition). An example implementation is provided by [Boost
|
||||
Fiber](https://www.boost.org/doc/libs/latest/libs/fiber/doc/html/index.html)
|
||||
which caters for (cooperative) switching between multiple fibers on a single
|
||||
thread. This is viable in C++ since such code should be exception-safe anyway
|
||||
and as such should not be taken by surprise upon code execution not making it
|
||||
all the way through a code sequence. After all, that might happen at any time
|
||||
upon exception, as in the particular case of fiber context destruction in stead
|
||||
of regular fiber termination. This approach also allows using legacy code in
|
||||
multiple fibers on a single thread, since no additional synchronization is
|
||||
required, and no special support (or keyword sprinkling) is needed along the
|
||||
callstack (down to point of suspension). To come down to it, the async Gio
|
||||
example provides a `GMainContext` based `Fiber` scheduler to integrate and
|
||||
support using Fiber in a standard GLib mainloop setup. In particular, that
|
||||
allows turning `Gio`'s async calls into (apparent) blocking calls when run on a
|
||||
fiber, which then leads to a concise sequential code flow.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
A few simple but nice to have features of the generated code are:
|
||||
|
||||
* well structured and easily understood
|
||||
* it can be used either fully inline or have implementation code compiled
|
||||
into one or more object files for subsequent linking
|
||||
* it allows for extensions/overrides (similar to e.g. PyGObject)
|
||||
supplied by separate files (so not in the generated ones),
|
||||
either default ones supplied along with this repo or added custom by
|
||||
the project using the generated code
|
||||
|
||||
Another feauture might be handy even if one is not interested in generated
|
||||
C++ code. As the `.gir` files as processed, a number of consistency checks
|
||||
are performed and warned about. In that way, the generator also acts somewhat
|
||||
as annotation validator, and can typically spot some missing `(out)` or
|
||||
`(array)` (and related) annotations.
|
||||
|
||||
Some other features have been implicitly mentioned above, but are perhaps best
|
||||
high-lighted by addressing how it differs from [gktmm](https://www.gtkmm.org)
|
||||
(and the related question; why another C++ interface). To this end, first
|
||||
note or recall that gtkmm consists of many repositories (glibmm, gtkmm,
|
||||
gstreamermm, ...), all of which typically also yield a distribution
|
||||
package for library code and headers. So, when using e.g. gstreamermm, several
|
||||
such packages are required, either for their headers at compile time or for the
|
||||
libraries at runtime. In contrast, only 1 repo is needed here, and using the
|
||||
generated code then incurs no additional (runtime) dependencies other than the
|
||||
(unavoidable) C libraries.
|
||||
|
||||
The code in gtkmm's (and friends') libraries can be considered as hand-crafted.
|
||||
Well, it is produced based on templates, but those are manually maintained. It
|
||||
does not use or consider the GObject-Introspection annotations in any way (not
|
||||
in the least because it predates that system). So, as a C library and API
|
||||
evolves, the corresponding gtkmm has to be manually synchronized. That is, if
|
||||
there is at least a corresponding gtkmm one. For a not-so-well-known-or-popular
|
||||
one there may not be, and definitely not for a custom developed one. On the
|
||||
other hand, whenever (minimal) annotations are available, you are good to go
|
||||
with any binding, whether PyGObject or the one provided here.
|
||||
|
||||
Since the gtkmm code is hand-crafted API from the ground up, that allows for
|
||||
some nifty things (such as the signal approach mentioned further below). On the
|
||||
other hand, sometimes it might be too nifty in that the C++ API does not quite
|
||||
track or match the original one. Instead, it is then more of an alternative or
|
||||
parallel one. For example, in GStreamer, there is (only) one (mini-object)
|
||||
C-type of `GstEvent` (with a type field indicating the precise type of event).
|
||||
So it is handled this way throughout the API and in bindings (e.g. PyGObject).
|
||||
However, in gstreamermm it has been chosen to have many types (i.e. subclasses)
|
||||
of `Gst::Event`. This may well be a natural C++ way (or a Python one if done
|
||||
with Python classes), but all together it presents an API where things are not
|
||||
quite where expected, and it is in that regard not a straight binding. In
|
||||
contrast, the generated code here is as straight as can be, and functions to
|
||||
call are right where the fingers expect them to be, whether in PyGObject or
|
||||
here.
|
||||
|
||||
So, in the concept of "C++ binding API", gtkmm has emphasis on C++
|
||||
up to the point of almost a parallel or independent API with quite some
|
||||
trimming. The latter is illustrated by the separate
|
||||
[libsigcplusplus](https://github.com/GNOME/libsigcplusplus) (with similar
|
||||
notions in e.g. [boost signals2](https://github.com/boostorg/signals2)). Here,
|
||||
however, the emphasis is on binding.
|
||||
|
||||
Of course, other than this straight binding, one is still free to use and bring
|
||||
in any other lib, e.g. one of the aforementioned signal helper libs. However,
|
||||
in most cases the "straight bindings" will suffice. Moreover, a few small
|
||||
additional RAII helpers are provided that may be useful. See the examples for
|
||||
an illustration on how they can be used.
|
||||
|
||||
## Limitations and Remarks
|
||||
|
||||
A compile-time binding is somewhat different than a typical more runtime script
|
||||
language binding (e.g. Python). While most of the annotated API is usually
|
||||
well enough handled and covered for practical use, there are some limitations
|
||||
and issues to consider. Again, more details and further remarks are provided
|
||||
in the [manpage](docs/cppgir.md)
|
||||
25
cmake/external/glib/cppgir/cmake/cmake-uninstall.cmake.in
vendored
Normal file
25
cmake/external/glib/cppgir/cmake/cmake-uninstall.cmake.in
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# from https://gitlab.kitware.com/cmake/community/-/wikis/FAQ
|
||||
# short unix alternative;
|
||||
# xargs rm < install_manifest.txt
|
||||
|
||||
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
|
||||
endif()
|
||||
|
||||
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
|
||||
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||
foreach(file ${files})
|
||||
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
|
||||
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
exec_program(
|
||||
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RETURN_VALUE rm_retval
|
||||
)
|
||||
if(NOT "${rm_retval}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
|
||||
endif()
|
||||
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
|
||||
endif()
|
||||
endforeach()
|
||||
29
cmake/external/glib/cppgir/cmake/cpp20_coro.cpp
vendored
Normal file
29
cmake/external/glib/cppgir/cmake/cpp20_coro.cpp
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <coroutine>
|
||||
|
||||
struct promise;
|
||||
|
||||
struct coroutine : std::coroutine_handle<promise>
|
||||
{
|
||||
using promise_type = ::promise;
|
||||
};
|
||||
|
||||
struct promise
|
||||
{
|
||||
coroutine get_return_object() { return {coroutine::from_promise(*this)}; }
|
||||
std::suspend_always initial_suspend() noexcept { return {}; }
|
||||
std::suspend_always final_suspend() noexcept { return {}; }
|
||||
void return_void() {}
|
||||
void unhandled_exception() {}
|
||||
};
|
||||
|
||||
static coroutine
|
||||
f()
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
7
cmake/external/glib/cppgir/cmake/cpp20_format.cpp
vendored
Normal file
7
cmake/external/glib/cppgir/cmake/cpp20_format.cpp
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
#include <format>
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
(void)std::format("");
|
||||
return 0;
|
||||
}
|
||||
4
cmake/external/glib/cppgir/cmake/cppgir-config-deps.cmake
vendored
Normal file
4
cmake/external/glib/cppgir/cmake/cppgir-config-deps.cmake
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(expected-lite)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppgir-targets.cmake")
|
||||
1
cmake/external/glib/cppgir/cmake/cppgir-config.cmake
vendored
Normal file
1
cmake/external/glib/cppgir/cmake/cppgir-config.cmake
vendored
Normal file
@@ -0,0 +1 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppgir-targets.cmake")
|
||||
9
cmake/external/glib/cppgir/cmake/cppgir.pc.in
vendored
Normal file
9
cmake/external/glib/cppgir/cmake/cppgir.pc.in
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
includedir=${prefix}/include/@PROJECT_NAME@
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: GObject Introspection C++ wrapper generator.
|
||||
Version: @PROJECT_VERSION@
|
||||
# unfortunately, no pkg-config on nonstd-expected
|
||||
# so no Requires if used as external dependency
|
||||
Cflags: -I${includedir} -I${includedir}/override
|
||||
72
cmake/external/glib/cppgir/conanfile.py
vendored
Normal file
72
cmake/external/glib/cppgir/conanfile.py
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
import os
|
||||
|
||||
from conan import ConanFile
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
|
||||
|
||||
class CppGirConan(ConanFile):
|
||||
version = "2.0.0"
|
||||
name = "cppgir"
|
||||
description = "gobject-introspection C++ binding generator"
|
||||
license = "MIT Software License"
|
||||
url = "https://gitlab.com/mnauw/cppgir.git"
|
||||
exports_sources = "data/*", "docs/*", "expected-lite/*", \
|
||||
"gi/*", "override/*", "tools/*", "test/*", \
|
||||
"examples/*", "!examples/external/*", \
|
||||
"CMakeLists.txt", "cmake/*", "LICENSE", "README.md"
|
||||
# ignore data depends on OS
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
build_policy = "missing"
|
||||
author = "Mark Nauwelaerts"
|
||||
test_package_folder = "examples/external"
|
||||
|
||||
options = {"header_only": [True, False]}
|
||||
|
||||
# default host dependency case
|
||||
default_options = {"header_only": True}
|
||||
|
||||
def requirements(self):
|
||||
if not self.options.header_only:
|
||||
self.requires("boost/[>=1.58]", options={'header_only': True})
|
||||
self.requires("fmt/[>=8.1.1]")
|
||||
|
||||
def layout(self):
|
||||
cmake_layout(self, build_folder="build.conan")
|
||||
# adjust for editable
|
||||
self.cpp.source.includedirs = ["."]
|
||||
# self.cpp.build.bindir = "."
|
||||
|
||||
def generate(self):
|
||||
tc = CMakeToolchain(self)
|
||||
tc.variables["BUILD_EXAMPLES"] = False
|
||||
tc.variables["BUILD_DOC"] = False
|
||||
# cmake --install relocation does not play well with ignore paths
|
||||
# so have those compiled in
|
||||
tc.variables["BUILD_EMBED_IGNORE"] = True
|
||||
tc.variables["BUILD_TOOLS"] = not self.options.header_only
|
||||
tc.generate()
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure()
|
||||
cmake.build()
|
||||
if not self.conf.get("tools.build:skip_test", default=False):
|
||||
test_folder = os.path.join(".")
|
||||
# test depends on GLib, which may not be present
|
||||
for t in ["gi-test", "gi-test-17"]:
|
||||
test = os.path.join(test_folder, t)
|
||||
if os.path.exists(test):
|
||||
self.run(test)
|
||||
|
||||
def package(self):
|
||||
"""Run CMake install"""
|
||||
cmake = CMake(self)
|
||||
cmake.install()
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.includedirs = ["include/cppgir"]
|
||||
|
||||
def package_id(self):
|
||||
# options access not allowed here, unfortunately
|
||||
if False and self.options.header_only:
|
||||
self.info.clear()
|
||||
181
cmake/external/glib/cppgir/data/cppgir.ignore
vendored
Normal file
181
cmake/external/glib/cppgir/data/cppgir.ignore
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
# generic
|
||||
.*_unref
|
||||
.*_free
|
||||
.*_ref
|
||||
.*_ref_sink
|
||||
|
||||
# GLib
|
||||
GLib:constant:LOG_DOMAIN
|
||||
GLib:record:Error
|
||||
# deprecated
|
||||
GLib:function:g_assert_warning
|
||||
GLib:function:g_slice_.et_config
|
||||
GLib:function:g_variant_get_gtype
|
||||
GLib:function:g_thread_init_with_errorcheck_mutexes
|
||||
# GVariantIter apparently represents 2 distinct types;
|
||||
# + a C-boxed type (used with _init)
|
||||
# + a heap-based behaving like GBoxed (with _copy and _free)
|
||||
# however, annotations do not cover/mention anything of the latter
|
||||
# so drop those parts here
|
||||
GLib:method:g_variant_iter_new
|
||||
GLib:method:g_variant_iter_copy
|
||||
# too tricky with generic signature
|
||||
GLib:method:g_source_set_callback.*
|
||||
# annotation problem
|
||||
GLib:function:g_strv_contains.*
|
||||
GLib:function:g_unichar_to_utf8
|
||||
# not needed; cause trouble otherwise
|
||||
#GLib:record:ByteArray
|
||||
#GLib:record:Bytes
|
||||
GLib:record:PtrArray
|
||||
GLib:record:Array
|
||||
GLib:record:S?List
|
||||
GLib:record:HashTable
|
||||
GLib:record:HashTableIter
|
||||
GLib:record:Queue
|
||||
# annotation problem
|
||||
GLib:VariantType:string_scan
|
||||
# conditional constants
|
||||
GLib:constant:macro__has_attribute.*
|
||||
GLib:constant:.*GETTEXT_DOMAIN
|
||||
GLib:constant:WIN32.*
|
||||
# not defined for C++ compiler
|
||||
GLib:constant:.*C_STD_VERSION*
|
||||
|
||||
# GObject
|
||||
GObject:record:Value
|
||||
# deprecated
|
||||
GObject:record:ValueArray
|
||||
|
||||
# GIO
|
||||
# deprecated
|
||||
Gio:interface:DesktopAppInfoLookup
|
||||
Gio:method:g_notification_set_urgent
|
||||
Gio:method:g_settings_list_keys
|
||||
# annotation problem
|
||||
Gio:virtual-method:ask_question
|
||||
# not covered by header includes
|
||||
Gio:function:g_networking_init
|
||||
# external plugin module API
|
||||
Gio:method:g_io_module_(load|unload)
|
||||
Gio:function:g_io_module_query
|
||||
|
||||
# private parts of the above; these should not make into the GIRs
|
||||
# but they might if gobject-introspection was built with embedded glib (meson wrapper)
|
||||
GModule:constant:MODULE_IMPL_.*
|
||||
GLib:constant:TRACE_.*
|
||||
GLib:function:trace_.*
|
||||
GLib:function:set_prgname_once
|
||||
Gio:function:to_rrtype
|
||||
Gio:class:ThreadedResolver
|
||||
GObject:bitfield:IOCondition
|
||||
|
||||
# Gst
|
||||
Gst:constant:ERROR_SYSTEM
|
||||
Gst:callback:DebugFuncPtr
|
||||
# GstBase
|
||||
# actually macros, but with gtk-doc comment
|
||||
GstBase:method:gst_byte_writer_put_buffer
|
||||
GstBase:method:gst_bit_writer_get_remaining
|
||||
# missing G_BEGIN_DECLS in video-blend.h header in some versions
|
||||
# (leads to C/C++ link mismatch)
|
||||
GstVideo:function:gst_video_blend_scale_linear_RGBA
|
||||
GstVideo:function:gst_video_blend
|
||||
# likewise missing G_BEGIN_DECLS in gstaudioiec61937.h
|
||||
GstAudio:function:gst_audio_iec61937_frame_size
|
||||
GstAudio:function:gst_audio_iec61937_payload
|
||||
# GstNtpClock is child of GstNetClientClock
|
||||
# but it uses the same C struct type
|
||||
GstNet:class:NtpClock
|
||||
|
||||
# Gtk and lower layers
|
||||
#
|
||||
# deprecated
|
||||
GdkPixbuf:record:Pixdata.*
|
||||
GdkPixbuf:bitfield:Pixdata.*
|
||||
GdkPixbuf:constant:PIXBUF_MAGIC_NUMBER
|
||||
GdkPixbuf:constant:PIXDATA_HEADER_LENGTH
|
||||
|
||||
# wrong annotation
|
||||
cairo:function:image_surface_create
|
||||
# repeated as GdkRectangle
|
||||
cairo:record:RectangleInt
|
||||
|
||||
# from generated code
|
||||
Pango:record:ScriptForLang
|
||||
|
||||
# in a header without extern C guard
|
||||
Atk:function:get_major_version
|
||||
Atk:function:get_minor_version
|
||||
Atk:function:get_micro_version
|
||||
Atk:function:get_binary_age
|
||||
Atk:function:get_interface_age
|
||||
|
||||
# private
|
||||
Gdk:method:destroy_notify
|
||||
Gdk:function:synthesize_window_state
|
||||
|
||||
# xlib GIR does not specify header
|
||||
# and including that one makes things really messy
|
||||
# (due to all sorts of define's)
|
||||
xlib:.*
|
||||
|
||||
# recent GIR describes way more than it specifies headers
|
||||
# also pretty low level, so let's sidestep altogether
|
||||
HarfBuzz:.*
|
||||
|
||||
# likwise so for freetype2
|
||||
freetype2:.*
|
||||
|
||||
# Gsk
|
||||
# deprecated
|
||||
Gsk:class:GLRenderer
|
||||
# in separate gtk4-broadway.pc
|
||||
Gsk:class:BroadwayRenderer
|
||||
|
||||
# likewise filter out some related GtkX parts
|
||||
# (should be in a separate ns btw for good measure)
|
||||
Gtk:class:Plug
|
||||
Gtk:class:Socket
|
||||
# private
|
||||
Gtk:method:gtk_widget_path_iter_add_qclass
|
||||
# missing in summary header gtk-a11y.h
|
||||
Gtk:class:HeaderBarAccessible
|
||||
Gtk:class:FileChooserWidgetAccessible
|
||||
# generated dbus sekeleton parts made it into GIR but otherwise missing
|
||||
Gtk:record:_MountOperation.*
|
||||
|
||||
# Gtk4
|
||||
# header not part of included headers
|
||||
Gtk:constant:IM_MODULE_EXTENSION_POINT_NAME
|
||||
# Gtk 4.9.1 deprecates TreeView and Cell Renderers
|
||||
# alternatives appear not so binding-friendly
|
||||
# so arrange to not discard these
|
||||
deprecated:Gtk:4.0
|
||||
|
||||
# misc config; specify substitute c:type for GtkSnapshot (defined in override)
|
||||
#!ctype:Gtk.Snapshot:GI_PATCH_GtkSnapshot
|
||||
|
||||
# signal emission method wrappers
|
||||
# undocumented; and should not be wrapped either
|
||||
# (with some problematic parameter types)
|
||||
Soup:method:soup_message_content_sniffed
|
||||
Soup:method:soup_message_wrote_chunk
|
||||
Soup:method:soup_message_wrote_headers
|
||||
Soup:method:soup_message_wrote_body
|
||||
Soup:method:soup_message_wrote_informational
|
||||
Soup:method:soup_message_got_chunk
|
||||
Soup:method:soup_message_got_headers
|
||||
Soup:method:soup_message_got_body
|
||||
Soup:method:soup_message_got_informational
|
||||
Soup:method:soup_message_starting
|
||||
Soup:method:soup_message_restarted
|
||||
Soup:method:soup_message_finished
|
||||
# deprecated interface since 13 years
|
||||
Soup:interface:PasswordManager
|
||||
# private
|
||||
Soup:function:soup_get_resource
|
||||
# unstable API
|
||||
Soup:class:Requester$
|
||||
Soup:method:soup_websocket_extension.*
|
||||
Soup:enumeration:RequesterError
|
||||
5
cmake/external/glib/cppgir/data/cppgir_unix.ignore
vendored
Normal file
5
cmake/external/glib/cppgir/data/cppgir_unix.ignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# items to ignore on UNIX platform
|
||||
|
||||
GLib:.*:.*WIN32.*
|
||||
GLib:.*:g_win32.*
|
||||
GLib:enumeration:Win32.*
|
||||
3
cmake/external/glib/cppgir/data/cppgir_win.ignore
vendored
Normal file
3
cmake/external/glib/cppgir/data/cppgir_win.ignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# items to ignore on WIN platform
|
||||
|
||||
GLib:.*:.*g_unix.*
|
||||
791
cmake/external/glib/cppgir/docs/cppgir.md
vendored
Normal file
791
cmake/external/glib/cppgir/docs/cppgir.md
vendored
Normal file
@@ -0,0 +1,791 @@
|
||||
cppgir(1) - GObject-Introspection C++ binding wrapper generator
|
||||
==================================================================
|
||||
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`cppgir` [OPTION...] `--output` _DIRECTORY_ GIR...
|
||||
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
`cppgir` reads each of the specified GIR and converts these (and any
|
||||
dependencies) into C++14 wrapper code that collectively then make up a
|
||||
'binding' (in
|
||||
[GObject-Introspection](https://wiki.gnome.org/Projects/GObjectIntrospection)
|
||||
terminology). Each GIR can be specified as a full pathname to the `.gir` file
|
||||
or simply by the basename (i.e. no path or `.gir` suffix), with or without
|
||||
version. Of course, in the latter case, the `.gir` must be in a standard
|
||||
location, or other options must specify additional whereabouts.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
See [BACKGROUND](#background) later on for further details on some of the
|
||||
concepts used in the following descriptions.
|
||||
|
||||
* `--output` _DIRECTORY_:
|
||||
Specifies the top-level directory in which to generate code.
|
||||
It will be created if it does not yet exist.
|
||||
|
||||
* `--gir-path` _PATHS_:
|
||||
Adds a colon-separated list of additional directories within which
|
||||
to (recursively) search for a `.gir` file (if not specified by full pathname).
|
||||
|
||||
* `--debug` _LEVEL_:
|
||||
Debug level or level of verbosity, higher numbers are more verbose.
|
||||
|
||||
* `--ignore` _FILES_:
|
||||
Adds a colon-separated list of so-called ignore files.
|
||||
|
||||
* `--suppression` _FILES_:
|
||||
Adds a colon-separated list of so-called suppression files.
|
||||
|
||||
* `--gen-suppression` _FILE_:
|
||||
Specifies a suppression file to generate during this run.
|
||||
|
||||
* `--class`:
|
||||
Requests generation of implementation class code needed for subclassing.
|
||||
|
||||
* `--class-full`:
|
||||
Requests generation of a plain as-is C signature fall-back method for
|
||||
an otherwise unsupported unwrapped method. Only applicable if
|
||||
`--class` is also specified. It also requires use of the latest custom
|
||||
subclass (signature) approach (see below for details on that),
|
||||
as these plain methods are not "activated" in case of legacy approach
|
||||
(for backwards compatibility).
|
||||
|
||||
* `--expected`:
|
||||
Use an error return type based on [std::expected](http://wg21.link/p0323) proposal
|
||||
(as opposed to throwing exception).
|
||||
|
||||
* `--dl`:
|
||||
Use dlopen/dlsym to generate (most) calls rather than usual "direct" calls.
|
||||
As such, a great many calls might then fail at runtime. So, if combined
|
||||
with `--expected` all those calls will use the above error return type.
|
||||
|
||||
* `--const-method`:
|
||||
Mark (almost) all generated methods in generated wrappers as `const`.
|
||||
Alternatively, perhaps more recommended, see also the helper `gi:cs_ptr` type.
|
||||
|
||||
* `--class-args` _MIN_OPTIONAL_:
|
||||
If >= 0, minimum number of non-required arguments that triggers generation of a
|
||||
`CallArgs` signature variant (see below for details).
|
||||
|
||||
* `--basic-container`:
|
||||
Also generate a collection signature for an input collection of basic type
|
||||
(e.g. `int`, etc). See below for some details and discussion.
|
||||
|
||||
* `--output-top`:
|
||||
Also generate convenience `.cpp` and `.hpp` files in root output directory
|
||||
per namespace, as may be useful for some build tool setups.
|
||||
|
||||
* `--dump-ignore`:
|
||||
(only if compiled with embedded ignore) Dumps embedded ignore data.
|
||||
|
||||
## ENVIRONMENT
|
||||
|
||||
In stead of command-line options, environment variables can also be used. Note,
|
||||
however, that options are still taken into account even when variables have
|
||||
been set. The following environment variables are considered, and have the same
|
||||
meaning as the corresponding command-line option:
|
||||
|
||||
`GI_DEBUG`, `GI_IGNORE`, `GI_SUPPRESSION`, `GI_GEN_SUPPRESSION`, `GI_OUTPUT`,
|
||||
`GI_CLASS`, `GI_CLASS_FULL`, `GI_EXPECTED`, `GI_DL`, `GI_GIR_PATH`
|
||||
|
||||
In addition to the above, `GI_GIR` can specify a colon-separated lists of GIRs
|
||||
(specified as on command-line). `XDG_DATA_DIRS` is also used as additional
|
||||
source of directories to search for GIRs (within a `gir-1.0` subdirectory).
|
||||
|
||||
|
||||
## BACKGROUND
|
||||
|
||||
### API v2
|
||||
|
||||
Note that v2 API is somewhat different than previous API, so some porting of
|
||||
existing code may be needed. See also later section for a rationale and
|
||||
discussion on changes.
|
||||
|
||||
The generated code provides a straight binding as specified by the annotations,
|
||||
so everything is pretty much where expected, such as methods within classes
|
||||
in turn within namespaces. For example, all `GObject` types are within
|
||||
namespace `gi::repository::GObject`. With that in mind, it should be easy
|
||||
to use and navigate in generated code, along with following comments:
|
||||
|
||||
* As customary, anything within a `detail` or `internal` namespace is not meant
|
||||
for public use and subject to change. The top-level gi namespace defines
|
||||
a few things that make up public API which is meant to be stable
|
||||
(though at this stage of maturity no full guarantee is provided).
|
||||
|
||||
* Some generated code may have `_` (underscore) appended to it simply to avoid
|
||||
clashing with a reserved keyword (or a preprocessor definition). It has
|
||||
no special (reserved) meaning otherwise.
|
||||
|
||||
* However, anything with leading underscore (if encountered) should be considered
|
||||
as internal (and not meant for public API).
|
||||
|
||||
In overall, the generated code is very lightweight and clear, easily understood
|
||||
and with little runtime overhead, as also illustrated by the following
|
||||
overview of wrappers for various kinds of types. Note that almost all of
|
||||
them essentially wrap a pointer and therefore should be checked for validity
|
||||
prior to many uses as with any "smart pointer"
|
||||
(e.g. using provided `operator bool()`).
|
||||
|
||||
**Objects.**
|
||||
A GObject is a single pointer along with class code that manages a single
|
||||
refcount (including decrement upon destruction). The refcount it manages is
|
||||
either received/taken from a `full` transfer, or `ref_sink`'ed (in case of
|
||||
`none`/`floating` transfer, see also discussion in subsequent section on the
|
||||
intricacies of the latter and theoretical edge cases).
|
||||
|
||||
**Boxed Types.**
|
||||
Similarly, but with a minor twist, wrappers for a boxed GType `MyBox` come in 2
|
||||
kinds; an owning `MyBox` and a non-owning `MyBox_Ref`. In both cases, the
|
||||
wrapper is again a single pointer with some suitable/applicable helper methods.
|
||||
The former essentially acts a "unique ptr" (with `g_boxed_free` deleter) whereas
|
||||
the latter acts as a "naked ptr/reference" (without any ownership or cleanup).
|
||||
Obviously, for the latter case, all the usual caution regarding dangling
|
||||
references (etc) applies. The latter are used for transfer `none` cases and
|
||||
the former in transfer `full` situations. In case a safe "reference" needs to
|
||||
be kept around (e.g. in some member), then a `_Ref` can be `.copy_()`'d (which
|
||||
uses `g_boxed_copy`) to an owning wrapper. The above semantics also imply that
|
||||
the owning wrapper is move-only (and again `.copy_()` yields a copy). However,
|
||||
there are quite some cases where a boxed copy is based on a refcount (which also
|
||||
preserves the box identity/pointer). Those cases have been specially marked (in
|
||||
overrides) to make the owning wrappers copyable as well. Likewise, a `_Ref` of
|
||||
such cases can be (implicitly) assigned/copied to an owning one (in each case
|
||||
triggering a `g_boxed_copy` which is then known to be plain and cheap).
|
||||
If desired, additional wrappers could be marked as copyable, in which case a
|
||||
wrapper copy invokes a potentially more expensive (and non-identity preserving)
|
||||
`g_boxed_copy`. Also, or alternatively, if `GI_ENABLE_BOXED_COPY_ALL` is
|
||||
defined and truthy, then all boxed wrappers are copyable in that way.
|
||||
|
||||
**Record Types.**
|
||||
Plain records (i.e. structs with no registered GType) are handled in a similar
|
||||
fashion, with `g_free` as "deleter" (and without any copy support). Since no
|
||||
lifecycle resource management (construction, destruction) is available for such
|
||||
types, there are (quite some) limitations to what code generation or binding can
|
||||
do here (see also discussion in corresponding section).
|
||||
|
||||
**Strings.**
|
||||
A string (e.g. `char*`) is also regarded and wrapped in a similar way. That is,
|
||||
a `gi::cstring` wraps (and owns and manages) a C `char*` and `gi::cstring_v` is
|
||||
the corresponding non-owning variant. Obviously, the former bears resemblance
|
||||
to `std::string` whereas the latter to `std::string_view`. In fact, as there is
|
||||
no real definitive "string API" (in C or glib), their API is fairly similar
|
||||
(though not guaranteed identical) to the `std` counterparts. Also, various
|
||||
conversions from/to `std` counterparts should allow for convenient type
|
||||
interchange. Additional integration with other string types is also possible by
|
||||
further specialization of `gi::convert::converter` (see `gi/string.hpp` source
|
||||
for details).
|
||||
|
||||
**Collections**.
|
||||
That is, `GList`, `GSList`, `GPtrArray`, `GHashTable` or plain arrays
|
||||
(zero-terminated or not). Similar to `std` container, each collection wrapper
|
||||
is a templatized `gi::Collection` type, with (a.o.) a type parameter for the
|
||||
contained type. As with some of the above types, such wrappers come in an
|
||||
owning and non-owning variants, as specified by another (type) parameter and
|
||||
obtained from annotations, i.e. transfer `none`, transfer `container` or transfer
|
||||
`full`. Note that the "ownership" specifies both ownership of the container and
|
||||
of the contained elements. Of course, where needed, code generation will select
|
||||
and specify the proper type (e.g. as function parameter). Following aspects
|
||||
are worth mentioning;
|
||||
|
||||
* Templatized constructors and conversion operators support construction
|
||||
from/of and assignment from/to (e.g.) `std` container types. Likewise
|
||||
so for "similar" (duck-ed) types, where "similar" refers to member types and
|
||||
constructor signatures.
|
||||
|
||||
* A (`std`) container-ish API is also provided, though neither identical
|
||||
nor fully compatible (a.o. due to limitations of the C wrappee's API).
|
||||
However, the `none` (ownership) variant is considered read-only and so
|
||||
it does not provide any "modification" API parts and only a `const` iterator.
|
||||
As almost no wrapper methods are `const`, an `auto p : coll` (range-for)
|
||||
pattern is recommended (wrappers are cheaply copied). Other variants do
|
||||
support modification as well as iteration that allows for a `auto &p : coll`
|
||||
pattern (if so desired). In particular, this applies to the `full` variant,
|
||||
which is the recommended one for "standalone" use (as container), as it
|
||||
safely manages ownership of both itself and elements.
|
||||
|
||||
* Wrappers of refcounted collections (`GPtrArray`, `GHashTable`) are
|
||||
otherwise similar to object wrappers. So they *always* manage a refcount (and
|
||||
are copyable) regardless of ownership variant (none, etc). The other wrappers
|
||||
are similar to boxed wrappers, e.g. copyable in `none` variant, but otherwise
|
||||
assume unique ownership and are non-copyable.
|
||||
|
||||
* A `gi::CollectionParameter` may also used by code generation for a function
|
||||
input parameter. In case of `none` ownership, this type/instance will
|
||||
temporarily hold ownership of a collection that may be created by conversion
|
||||
from another container. Temporarily here refers to the duration of the call
|
||||
during which the parameter instance exists. It is not (and should not be)
|
||||
used elsewhere.
|
||||
|
||||
* For an input collection parameter of basic type (e.g. `int`), the
|
||||
original C signature is typically used. That is, 2 parameters (`int*` and
|
||||
`gsize`). The rationale here is that the same signature may also occur for an
|
||||
output collection (of specified input size). Preserving the original signature
|
||||
ensures that it can be used whether or not the annotation is correct. The
|
||||
latter may not be the case as these APIs are typically "low-level" (e.g. involve
|
||||
some buffers), and as such are often not considered by "scripted binding". It is
|
||||
also an efficient and clear API as any buffer's "location" (e.g. `std::vector`
|
||||
or otherwise) can easily be provided by means of these 2 parameters.
|
||||
However, if desired and specified by `--basic-container` option, then an
|
||||
additional collection-based signature is generated as well.
|
||||
|
||||
In short, one can choose to work with `std` types and convert to
|
||||
collection wrappers upon function call/return, but for simple cases (or beyond),
|
||||
the collection wrapper might well serve (without conversion).
|
||||
|
||||
**Plain Types.**
|
||||
Various enum, (static) method, functions, typedef (for callback) fill in the
|
||||
rest.
|
||||
|
||||
**Functions.**
|
||||
Functions that involve the usual `GError` return pattern are wrapped in a few
|
||||
ways. On the one hand, in a straight way, where the error is a (wrapped error)
|
||||
output parameter. Alternatively, the error parameter is removed from the
|
||||
signature. In that case it is "returned" by either throwing the (wrapped) error
|
||||
(which is also a `std::exception` subclasss), or by returning a suitable
|
||||
`expected` type (with the wrapped error type as error type). While throwing is
|
||||
default behaviour, the latter can be requested using `--expected` option.
|
||||
|
||||
In case of a `GError` in (function) callback or virtual method signature, it is
|
||||
always retained as a (wrapped) error output parameter and preferably used to
|
||||
report an error that way. Alternatively, an exception can be thrown, preferably
|
||||
then a `GLib::Error` instance. Callback wrapping code will catch any exception
|
||||
and report (to `C` caller) using `GError` output along with a zero-initialized
|
||||
return value, which is likely but not necessarily a good choice.
|
||||
|
||||
Note, however, that the aforementioned `catch` only applies if exception support
|
||||
is enabled. Auto-detection of this should usually work, but if needed can be
|
||||
specified by defining `GI_CONFIG_EXCEPTIONS` explicitly (truth/falsy).
|
||||
|
||||
If a function has (non-GError) output parameters, then there is a signature
|
||||
where these outputs are parameters (as in the plain C case) and another variant
|
||||
where these are incorporated in the return value, which may then become a
|
||||
`std::tuple<>` type.
|
||||
|
||||
If so configured, some so-called `CallArgs` variations may also be generated. In
|
||||
this case, (roughly) a custom `xyz_CallArgs` struct type is generated (for each
|
||||
function `xyz`) with members corresponding to the function arguments along with
|
||||
a function with 1 argument (of that custom struct type). In case of many
|
||||
(optional) arguments, this argument could be specified using designated
|
||||
initializer syntax, thereby allowing a sort-of "call by keyword". Again, there
|
||||
are variations with output parameters as return value or not. Besides some
|
||||
potential advantages, there are also some drawbacks, however. First and
|
||||
foremost, when using the member names in designated initializers, these names
|
||||
then become essentially part of the API, although the name's origin as
|
||||
plain C function parameter does not provide stability guarantee. Also,
|
||||
suffice it to say a great many struct types can be generated this way. This could
|
||||
be mitigated by employing a suitable "generation level", e.g. 2. In that case,
|
||||
only functions that have at least 2 (or more) non-required arguments will have
|
||||
such a custom type and signature generated.
|
||||
|
||||
**Subclasses and Interfaces.**
|
||||
Some additional specifications on how subclasses and interfaces are mapped
|
||||
may also be in order. A subclass in the GObject world is directly mapped as a
|
||||
subclass in the C++ binding. However, if a GObject implements an interface, the
|
||||
generated class does not inherit from the interface's (generated) class. This
|
||||
is mostly of a matter of implementation choice (and to ensure its lightweight
|
||||
simplicity). However, knowledge of implemented interfaces is not always
|
||||
available at compile time, e.g. in case of dynamically loaded GStreamer
|
||||
elements (though it is more likely in case of Gtk hierarchy). Since there would
|
||||
be no inheritance in the dynamic case, a consistent choice is not to have it at
|
||||
any time. However, for ease of use, some helper code is generated when an
|
||||
implemented interface is known at generation/compile time, as illustrated in
|
||||
the following snippet from an example
|
||||
|
||||
// use a cast if not known, either to a class or interface
|
||||
auto bin = gi::object_cast<Gst::Bin>(playbin_);
|
||||
// known at compile time; overloaded interface_ method
|
||||
auto cp = bin.interface_ (gi::interface_tag<Gst::ChildProxy>());
|
||||
|
||||
|
||||
### SUBCLASS IMPLEMENTATION API
|
||||
|
||||
There may be times when one would want to make a custom subclass of GObject, or
|
||||
of some Gtk widget. In the same vein, (current) implementation choices imply
|
||||
that one should not simply inherit from `Gtk::Window`. Part of the motivation
|
||||
here is that such subclassing depends on style and setting, i.e. it is rather
|
||||
rare when in a GStreamer setting, but less so in e.g. Gtk. As such, the
|
||||
possibly rare cases should not burden or complicate the basic wrapping usecase.
|
||||
|
||||
Before going into the details of "how", let's first consider what a "subclass"
|
||||
actually means in this context. It will probably involve a (C++) (sub)class of
|
||||
some generated class (related to a GObject class type) with potentially
|
||||
extra/custom properties and signals. In particular, the latter implies it also
|
||||
involves a new `GType` that defines a subtype (of the aforementioned GObject
|
||||
class type). An instance of such class/type then consists of a C++ instance
|
||||
that is 1-to-1 associated with a GObject instance of the custom defined (sub)
|
||||
`GType`.
|
||||
|
||||
In turn, this leads to (at least, for now) 2 possible ways to create instances;
|
||||
|
||||
* triggered on C++ side, in the usual way through a constructor. The bottom
|
||||
of the constructor chain registers a custom `GType` (if still needed), creates
|
||||
an instance (`g_object_new()`) and associates it (with the C++ instance).
|
||||
|
||||
The advantage is that standard use of C++ constructor applies. The downside
|
||||
is that the defined `GType` can not be (safely) used in the GObject
|
||||
(eco)system, as any `g_object_new()`'d instance is incomplete (as it lacks a
|
||||
C++ instance).
|
||||
|
||||
* triggered on C-side by `g_object_new()`. In this case, the registered type's
|
||||
custom (GObject) `constructor` first delegates to parent constructor to create
|
||||
a GObject subtype instance and then `new`'s a C++ object, and again the
|
||||
bottom of the constructor chain associates it with created GObject instance.
|
||||
|
||||
In this case, an instance has to be created based on `GType`. However, this
|
||||
type can be safely used in any `GType` based factory system, e.g. when
|
||||
referenced (by name) in XML/UI file or (by number) in a GStreamer plugin
|
||||
factory.
|
||||
|
||||
In summary, (up to) 2 different ways to create objects, and so (up to) 2
|
||||
different custom `GType` that can be defined by a "subclass". The latter one
|
||||
is more recent and is also the recommended approach as instances are always
|
||||
properly created. Also, if desired, some additional helper `new_()` member
|
||||
can be defined that acts as a surrogate constructor.
|
||||
|
||||
So, how to subclass then? By a slight twist by using the `impl` namespace
|
||||
variations, as in following excerpt from an example:
|
||||
|
||||
class TreeViewFilterWindow : public Gtk::impl::WindowImpl
|
||||
{
|
||||
// ...
|
||||
public:
|
||||
// Assume (hypothetically) that Window also implements FakeInterface
|
||||
// with a set_focus method, then a compilation failure will be triggered (as
|
||||
// it can no longer be detected whether set_focus is defined in this class).
|
||||
// Then the following inner struct is needed to resolve so manually;
|
||||
struct DefinitionData
|
||||
{
|
||||
// the last parameter specifies whether the method is defined
|
||||
// (which may well be false in all class/interface cases if not defined)
|
||||
GI_DEFINES_MEMBER(WindowClassDef, set_focus, true)
|
||||
GI_DEFINES_MEMBER(FakeInterfaceDef, set_focus, false)
|
||||
};
|
||||
// NOTE for the auto-detection to work, the methods must be accessible
|
||||
// so either they should be defined public, or (e.g.) WindowClassDef
|
||||
// must be declared friend, or the above manual resolution can be used.
|
||||
|
||||
// this (super)constructor signature leads to a C++ side type
|
||||
TreeViewFilterWindow () : Gtk::impl::WindowImpl (this)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
// this (super)constructor signature leads to a C-side type
|
||||
// NOTE InitData is not (easily) instantiated,
|
||||
// so this constructor is only used by the internal C-side mechanics
|
||||
TreeViewFilterWindow (const InitData &id)
|
||||
: Gtk::impl::WindowImpl (this, id, "TreeViewFilterWindow")
|
||||
|
||||
void set_focus_ (Gtk::Widget focus) noexcept override
|
||||
{
|
||||
}
|
||||
|
||||
// the above constructor is also re-used during (C-side) type registration
|
||||
// (in that case with an "empty" id and no associated GObject setup)
|
||||
// that can avoided by providing a separate ...
|
||||
GType get_type_()
|
||||
{
|
||||
// no interfaces, properties or signals to declare
|
||||
return register_type_<TreeViewFilterWindow>("TreeViewFilterWindow", 0, {}, {}, {});
|
||||
}
|
||||
};
|
||||
|
||||
// create an instance of (either) type
|
||||
// it prefers the latter C-side type, if supported, and supports extra arguments
|
||||
// (a second template parameter allows for specific selection,
|
||||
// see code comments and examples for details)
|
||||
gi::make_ref<TreeViewFilterWindow>()
|
||||
|
||||
Parent (class or interface) methods can then be overridden or implemented
|
||||
in the usual way by simply defining them in the subclass. It is also possible
|
||||
to define custom signal and properties in the subclass, as illustrated in the
|
||||
`gobject.cpp` example. As mentioned, the inner `DefinitionData` struct in the
|
||||
above fragment is usually not needed, but only in case of conflict/duplication
|
||||
of class/interface member(s).
|
||||
|
||||
Since this is considered an optional feature, the `impl` parts are not generated
|
||||
by default, but only if the `--class` option is specified. Since the virtual
|
||||
methods share some similarities with callbacks they are also subject to some
|
||||
limitations (see corresponding section). As such, it may happen that some
|
||||
virtual methods do not have a wrapper. If the `--class-full` option is
|
||||
specified, then a passthrough virtual method (with C signature as-is) is then
|
||||
generated instead, which can then be overridden and implemented as a fallback.
|
||||
So the custom type registration (that happens behind the scenes) can then still
|
||||
be used, albeit at the expense of dealing with a plain C signature and types
|
||||
(which is similar to directly calling a C function as a fallback if no wrapper
|
||||
function was generated for some reason).
|
||||
|
||||
It is also a fairly advanced feature, with various aspects not immediately
|
||||
obvious. For example, the resulting "instances" have "2 sides"; there is C++
|
||||
object instance as well as an associated (derived) GObject instance (which
|
||||
internally refer to each other in 1-to-1 association ). In particular, it
|
||||
follows that their destruction must be closely coordinated. This is potentially
|
||||
tricky as the GObject side is traditionally reference-counted, whereas the C++
|
||||
object could be anything (stack-allocated or otherwise). The latter can be
|
||||
stack-allocated if it is ensured that there is no lingering reference in the
|
||||
GObject world. So, it depends on the use-case as to how to proceed. But in
|
||||
overall, it is probably recommended to manage lifetime and ownership based on
|
||||
GObject (side) reference count. The `gi::make_ptr` and `gi::ref_ptr` helpers
|
||||
can be helpful in this regard.
|
||||
|
||||
There are also situations where the C API (implementation) involves some
|
||||
"low-level tricks" which do not port over in a simple or straight way (e.g.
|
||||
`GtkBuilder`). Those are likely in need of some custom overrides or extensions
|
||||
(see also below) which may or may not already be provided for some particular
|
||||
API/situation. It is highly advised to browse provided overrides and
|
||||
extensions and/or consult the closest relevant related example
|
||||
(which usually showcases what is available, along with additional explanations).
|
||||
|
||||
|
||||
### CODE LAYOUT AND BUILD SETUP
|
||||
|
||||
The generated code is written to the top-level with the following layout.
|
||||
Each GIR namespace has a corresponding subdirectory, say `ns`
|
||||
(and also a C++ namespace, `cppgir::repository::ns`). The top-levels
|
||||
headers for a namespace are then:
|
||||
|
||||
* `ns.hpp`:
|
||||
a regular header providing the namespace's declarations.
|
||||
It will also include the dependent namespaces' top headers.
|
||||
If the macro `GI_INLINE` is defined, then it will also include ...
|
||||
* `ns_impl.hpp`:
|
||||
contains the definitions corresponding to the declarations.
|
||||
Normally, this would be a `.cpp` file, but as they might be included directly
|
||||
in the inline case, they have been named `xxx_impl.hpp` instead.
|
||||
* `ns.cpp`:
|
||||
this merely includes `ns_impl.hpp` and is as such no different
|
||||
than the latter, except for more traditional naming.
|
||||
Compiling this file in the non-inline case provides all the definitions
|
||||
for the namespace in the resulting object file.
|
||||
* `*.cppm`; module wrappers, see next subsection for details.
|
||||
|
||||
So, in summary, it comes down to setting up the build system to build each of
|
||||
the namespaces' `.cpp`, as is also done in this repo's CMake build setup.
|
||||
There is one other shortcut build setup that is illustrated by the `gtk-obj.cpp`
|
||||
example file, which includes all definitions (recursively):
|
||||
|
||||
#define GI_INCLUDE_IMPL 1
|
||||
#include <gtk/gtk.hpp>
|
||||
|
||||
Note, however, this is only possible if there is exactly 1 top-level namespace,
|
||||
as doing this for several namespaces will lead to duplicate definitions.
|
||||
|
||||
Some items (functions, types) may be marked as deprecated (in source code).
|
||||
while still present in GIR data. Wrappers will still be generated and
|
||||
`pragma` are issued to avoid warnings that might otherwise occur.
|
||||
Generic `gi` support tries to avoid using deprecated code. There is, however,
|
||||
one exception regarding the use of `g_object_newv`, which is deprecated
|
||||
but may have to be used if support for an older GLib is required.
|
||||
This can be arranged by defining `GI_OBJECT_NEWV` (and the deprecation
|
||||
warning should also be silenced when dealing with newer version).
|
||||
If the items are also marked deprecated in GIR data, then these are skipped
|
||||
by default. However, if the string `deprecated:<NAMESPACE>:<VERSION>`
|
||||
matches (a regexp) in specified ignore data/files, then deprecated items
|
||||
will be considered for the namespace in question, after being checked as
|
||||
usual against the ignore list.
|
||||
|
||||
If you have specified the `--class` option, then the generated code will
|
||||
possibly contain classes that inherit from several classes (representing
|
||||
interfaces). Since various interfaces may have overlapping member names, this
|
||||
might trigger compilation warnings. These are not suppressed by default, as you
|
||||
may need to be made aware of this. However, if it does no harm in your
|
||||
particular case, then defining `GI_CLASS_IMPL_PRAGMA` should arrange for proper
|
||||
suppression.
|
||||
|
||||
The generated code may be quite extensive and so it may present a "heavy build".
|
||||
Other than that the above allows for a number of different build setups,
|
||||
`cppgir` tries not to impose any particular approach. In particular, standard
|
||||
tried-and-tested build optimizations can be applied, such as precompiled headers
|
||||
(with some good results, as reported in
|
||||
[issue #99](https://gitlab.com/mnauw/cppgir/-/issues/99)).
|
||||
|
||||
|
||||
**C++ Module support**
|
||||
|
||||
At this time of writing, module support is still new-ish in compilers
|
||||
and (not in the least) build tools. Unfortunately, all major compilers also
|
||||
take a different approach in handling these wrt command-line argument, source
|
||||
file extension or binary output. Note that this also likely complicates any
|
||||
`compile_commands.json` based tooling (e.g. LSP server as used by IDEs) along
|
||||
with other issues as outlined in
|
||||
[this post](https://nibblestew.blogspot.com/2023/12/even-more-breakage-in-c-module-world.html)
|
||||
and originally raised
|
||||
[long ago](https://vector-of-bool.github.io/2019/01/27/modules-doa.html).
|
||||
|
||||
By comparison, precompiled headers are long since well supported, so these may
|
||||
be a more recommended alternative approach.
|
||||
|
||||
Failing that, a next natural fit might be "header units" (in module spec sense).
|
||||
Unfortunately, compiler setup here varies somewhat and build tool support may be
|
||||
limited.
|
||||
|
||||
So, a next step is a pure/real module. Unfortunately, "core gi" can not be
|
||||
provided in that form;
|
||||
|
||||
* in addition to code, it also "exports" some macros, which can not really be
|
||||
`export`'ed or `import`'ed (only from a "header unit")
|
||||
* the core is somewhat intertwined with basic types from GObject,
|
||||
e.g. some code-generated types are forward declared (which satisfies
|
||||
for their purposes in "core gi"). However, forward and real declaration
|
||||
can not pass module boundary.
|
||||
|
||||
But "core gi" combined with (generated) GLib and GObject can be wrapped
|
||||
in a module (along with a separate "macro API header", `gi_inc.hpp`).
|
||||
|
||||
Again, `cppgir` does not impose a particular module layout/setup, but provides
|
||||
the basic parts and pieces to do so. The generated code also provides a few
|
||||
example module wrappers (with no stability guarantee), also see `gst.cpp` and
|
||||
`gtk.cpp` examples for possible usage details;
|
||||
|
||||
* `ns.cppm` (exports `gi.repo.ns`);
|
||||
module that wraps/provides `ns` (inline) code, so a typical compilation
|
||||
produces `.o` code of `ns` and a BMI/CMI that exports `ns` declarations
|
||||
* `ns_rec.cppm` (exports `gi.repo.ns.rec`);
|
||||
module that wraps/provides `ns` and all (recursive) dependencies, so a
|
||||
typical compilation produces `.o` code of `ns` and dependencies, along
|
||||
with a BMI/CMI with corresponding declarations.
|
||||
|
||||
The above have been tested with gcc-15 and clang-18 with some varied measure of
|
||||
success (apparently, GCC fails to link the non-recursive approach). They each
|
||||
come with an extra "knob"; if `GI_MODULE_EXTERN` is defined, then the module
|
||||
purview is essentially `extern "C++" { ... }`. So all declarations are then
|
||||
attached to global module, as opposed to the named module, and as such do not
|
||||
incur any modified ABI linkage (`@M`) (so it may be recommended).
|
||||
|
||||
Other variations are likely possible, e.g. module partitions, separate
|
||||
module implementation, etc.
|
||||
|
||||
|
||||
### OVERRIDING OR EXTENDING
|
||||
|
||||
It is possible to add functions or methods or override existing names (by
|
||||
effect of name hiding). To this end, the generated code contains various
|
||||
'optional include hooks' using the `__has_include` directive. This way, code in
|
||||
externally supplied (include) files can be inserted into the class definition
|
||||
chain. There are roughly 3 such 'hook points':
|
||||
|
||||
* **initial setup**:
|
||||
this part is (conditionally) included before the namespace's C headers are included.
|
||||
This allows specifying define's to tweak subsequent headers or to add
|
||||
headers that also need to be include'd, and which may not have been specified
|
||||
in the GIR.
|
||||
|
||||
* **class definition**:
|
||||
these hooks allow extending the wrapped class with new or tweaked methods
|
||||
|
||||
* **global extra definitions**:
|
||||
these are included after all generated code, and supports adding of new global
|
||||
functions, typedef's, type trait helper declarations, ...
|
||||
|
||||
The reader is invited to examine the default overrides in this repo as well as
|
||||
the generated code to see how this fits together based on a simple naming
|
||||
scheme and use of macros. In particular, see the provided `GLib` overrides.
|
||||
Suffice it to add that the `_def` suffix refers to 'default' as supplied by
|
||||
this repo and which are installed alongside the common headers. The
|
||||
corresponding non-suffixed filenames should be used by project specific custom
|
||||
additions.
|
||||
|
||||
|
||||
### CODE GENERATION
|
||||
|
||||
It might be necessary to exclude a GIR entry from processing, either because it
|
||||
is a basic type handled by custom code (e.g. `GObject`, `GValue`, ...) or
|
||||
because of a faulty annotation. The latter can be a glitch in the annotation
|
||||
itself, or one that actually refers to a symbol in a non-included private
|
||||
header. The exclusion can be directed by so-called ignore files, and at least
|
||||
one such is supplied as a system default ignore containing known and essential
|
||||
cases to exclude (and without which code generation would not produce valid
|
||||
code). Such a file consists of lines of regular expressions (`#` commented
|
||||
lines are ignored). At generation time, each symbol is turned into a
|
||||
`<NAMESPACE>:<SYMBOLKIND>:<SYMBOL>` string, and excluded if it matches one of
|
||||
the lines' regular expression. So, for instance, `GObject:record:Value`
|
||||
prevents processing of `GValue`, since there is already special-case code for
|
||||
that in the common header code. Further expression examples are found in the
|
||||
default ignore file. Additional files can be specified by the `--ignore`
|
||||
option.
|
||||
|
||||
As each entry is processed, some notification may be given regarding a
|
||||
perceived inconsistency in an annotation or an unsupported case (see also [BUGS
|
||||
AND LIMITATIONS](#bugs-and-limitations)). When the reported cases have been
|
||||
(manually) checked and considered harmless, the corresponding notices can be
|
||||
suppressed by specifying suppression files to `--suppression`. The format of
|
||||
such files is the same as ignore files, except that a match then simply serves
|
||||
to decrease reporting verbosity. Such a file could be hand-crafted, but it can
|
||||
also be auto-generated by a run when specifying `--gen-suppression`.
|
||||
|
||||
Besides excluding problematic GIR parts, one might also consider solutions to
|
||||
some problematic GIRs used by other projects, such as fixed GIRs maintained by
|
||||
[gtk-rs](https://gtk-rs.org/gir/book/tutorial/finding_gir_files.html#gtk-dependencies)
|
||||
in the referenced [repo](https://github.com/gtk-rs/gir-files).
|
||||
|
||||
### (RATIONALE OF) v2 CHANGES
|
||||
|
||||
Consider the following python session using gobject-introspection:
|
||||
|
||||
>>> import gi
|
||||
>>> gi.require_version('Gst', '1.0')
|
||||
>>> from gi.repository import Gst
|
||||
>>> Gst.init(None)
|
||||
>>> c = Gst.caps_from_string('video/x-raw')
|
||||
>>> c.get_structure(0)
|
||||
<Gst.Structure object at 0x7fe284096760 (GstStructure at 0x1bb4420)>
|
||||
>>> c.get_structure(0)
|
||||
<Gst.Structure object at 0x7fe2840b5d00 (GstStructure at 0x1bb43a0)>
|
||||
|
||||
What happens here? A different `GstStructure*` is created each time, even
|
||||
though the same one is returned (by C code) in each case. The python binding
|
||||
here has no other choice than to use `g_boxed_copy()` on the transfer `none`
|
||||
return value. If it would not, it would be carrying around an unguarded/unowned
|
||||
and hence potentially dangling pointer (in some `PyObject` wrapper), which is
|
||||
a definite no-go in a scripted setting that must always ensure valid objects.
|
||||
|
||||
v1 API followed a similary "scripted" style approach where all objects/pointers
|
||||
should always be safe and valid, with (roughly) `std::shared_ptr` in place of
|
||||
`PyObject`. Of course, also then with similar (copy) effects as in the above
|
||||
excerpt and in e.g. [issue #32](https://gitlab.com/mnauw/cppgir/-/issues/32).
|
||||
|
||||
v2 now follows a different approach. After all, C++ is much closer to C, and it
|
||||
is customary to mind about (potentially dangling) references and such, and where
|
||||
and how (not) to use e.g. `std::string_view`. And so while types/objects are
|
||||
now no longer always "owning" (and as such always safe), the type conventions do
|
||||
clearly specify whether or not they do (own). As such, standard C++ practices
|
||||
should handle what v2 API provides, while avoiding superfluous and potentially
|
||||
surprising copies or any other "automagic". In particular, the v2 bindings
|
||||
are therefore even more "tight and direct" than before, with a typical wrapper
|
||||
being only a cast away from the wrappee (and matching in size and semantics).
|
||||
|
||||
**Migration.**
|
||||
In practice, only limited changes have been needed in the included examples.
|
||||
Of course, your mileage may vary, depending on usage of "boxed types" as well
|
||||
as use of (type deduction) `auto` versus explicit type specification.
|
||||
Some `_Ref` types may have to be used instead here or there, as well as possibly
|
||||
some `std::move` on "owning" variants (unless overall boxed copy is enabled).
|
||||
For reasons of consistency and to avoid collision with generated methods,
|
||||
some more "custom methods" may have had `_` appended
|
||||
(e.g. `CBoxed::allocate_()`).
|
||||
|
||||
|
||||
## BUGS AND LIMITATIONS
|
||||
|
||||
The generated code's coverage is pretty good and comfortably serves most
|
||||
cases that arise in practice as also illustrated by the examples.
|
||||
Nevertheless, the following should be mentioned:
|
||||
|
||||
**Callback types.**
|
||||
Only callback types that have an explicit `user_data`
|
||||
parameter are supported. That includes (fortunately) cases such as connecting
|
||||
to a signal, or a `GstPadProbeCallback`, though a `GstPadChainFunction` is
|
||||
excluded. The reason is a technical one; the `user_data` parameter is used to
|
||||
pass data used by callback wrapper code. A typical (script) runtime binding
|
||||
handles this using [libffi](https://github.com/libffi/libffi)'s closure API. In
|
||||
effect, a little bit of executable code is then generated at runtime, and the
|
||||
address of that code then essentially serves as surrogate `user_data` that can
|
||||
carry extra meta-data for use by the runtime. This could also be employed here
|
||||
to lift the `user_data` limitation, it would take a bit extra work, but would
|
||||
more importantly then also incur an additional dependency.
|
||||
|
||||
**Callback handling.**
|
||||
Even if `user_data` is present, other aspects of a callback signature
|
||||
may not be supported (at this time), e.g. certain (sized) array parameters.
|
||||
However, few (if any) of such actual cases are known at this time.
|
||||
Note that both signals and virtual methods are somewhat similar to a callback
|
||||
and as such share similar limitations.
|
||||
|
||||
Whereas the above items could (in theory) be resolved, the following are more
|
||||
inherent limitations (by the very context and nature of e.g. annotations).
|
||||
Fortunately, though, the practical impact is fairly limited (if any).
|
||||
|
||||
**const handling.** In C++, this is a Bigger Thing. For instance, a simple
|
||||
'getter' should preferably be marked const. However, on the original C-side of
|
||||
things, only very limited consideration is given to this. Even if there is some
|
||||
`const`, it is not treated with all that much respect, e.g.
|
||||
`g_value_take_boxed` starts `const` but it is merrily cast away along the way.
|
||||
As such, there is not much to find on const-ness in annotation data, and so no
|
||||
point in inventing any. Rather, the focus is simply on getting the proper
|
||||
function calls done along with automagic refcount and resource management (much
|
||||
as any runtime binding would do, with no regard for const whatsoever in that
|
||||
case).
|
||||
|
||||
In particular, methods are usually not marked `const` either, as there is
|
||||
similarly no (semantic) data to decide either way. As such, it is not
|
||||
recommended to use `const` wrapper types. However, they may arise due to generic
|
||||
templates or when captured in a lambda. In such cases, the helper (template)
|
||||
type `gi::cs_ptr` may be useful, or alternatively one might set the option to
|
||||
mark all code-generated methods as `const`.
|
||||
|
||||
**Floating (into darkness).**
|
||||
[Gobject docs](https://docs.gtk.org/gobject/floating-refs.html) mention the
|
||||
following about floating references (i.e. transfer `floating`);
|
||||
|
||||
> Floating references are a C convenience API and should not be used in modern
|
||||
GObject code. Language bindings in particular find the concept highly
|
||||
problematic, as floating references are not identifiable through annotations,
|
||||
...
|
||||
|
||||
Indeed, by the time `floating` makes it into the parsed annotation, it has
|
||||
become `none`. And in case of a "factory" `some_widget_new()`, `floating`
|
||||
behaves more like `full` as the caller must "take ownership" to avoid a leak. So
|
||||
a "floating" `none` is quite different from a "real" `none` (e.g. "getter"
|
||||
method). But no way to know from annotation data. So, in case of `none`, an
|
||||
object wrapper always `ref_sink()`s. If it was floating, it has taken suitable
|
||||
ownership. If it was really none, then it is now managing an extra refcount.
|
||||
And in either case, it will release/decrement upon destruction. Essentially,
|
||||
this follows the recommendation given in referenced docs. In practice, it
|
||||
actually Just Works.
|
||||
|
||||
It gets really tricky when this is combined with e.g. lists. So what does `none`
|
||||
mean in this case (in annotation)? In the worst case, the contained elements
|
||||
might actually be floating, so one would have to go through the list and
|
||||
`ref_sink` them all (un)conditionally? Suffice it to say, no such "automagic"
|
||||
is handled/injected by any wrapper code. Fortunately, at this time there does
|
||||
not seem to be such a "multiple factory" API. Even if there were, then in
|
||||
practice the calling code is likely to loop over the list and access the
|
||||
elements. The ensuing C++ wrappers (even if existing only briefly) would then
|
||||
effectively `ref_sink()`, so again we are ok. And last but not least, by the
|
||||
above quoted recommendation, there should be no such new tricky API coming
|
||||
along. So, again, it Just Works. If needed, any such old or new API can and
|
||||
should be handled by custom overrides.
|
||||
|
||||
**Boxed (by darkness).**
|
||||
This refers to so-called "plain records" which are "C structs" with no
|
||||
registered `GType` (referred to as "C boxed" types in `cppgir` code), e.g.
|
||||
`GOptionEntry` or `GstMapInfo`. While their fields may be described in
|
||||
annotations, there is no information regarding the "ownership" of any data
|
||||
(which may even vary upon context). In particular, also no way to create/free.
|
||||
This corresponds with their frequent stack-allocated use in C code in typically
|
||||
"low-level" API which is usually not considered "binding friendly". Based on
|
||||
the mild assumption that 0-initialized data makes a valid instance, they are
|
||||
treated somewhat similar to (GType) boxed types and as such can be used in some
|
||||
limited (function call) situations. Any improvement beyond that is likely to
|
||||
remain in the purview of overrides.
|
||||
|
||||
|
||||
### WORKAROUNDS
|
||||
|
||||
As C++ allows direct mixing/calls with C, there are usually some fallback
|
||||
workarounds when confronted with one of the limitations. First of all,
|
||||
note that a C++ wrapper typically has e.g. a `gobj_()` method that
|
||||
provides the underlying C pointer/object. Conversely, `gi::wrap` can be
|
||||
used to obtain a wrapper from a C pointer/object obtained by some means.
|
||||
With that in mind, the following are some workarounds;
|
||||
|
||||
* function call;
|
||||
using/given the above, the C function can then (simply) be called directly
|
||||
|
||||
* custom subclass virtual method;
|
||||
use `--class-full` to generate a virtual method with plain C signature
|
||||
|
||||
* signal;
|
||||
use `Object::connect_unchecked` (see also `gst.cpp` example)
|
||||
|
||||
* callback;
|
||||
use `gi::callback_wrapper` (see also in same example location as above).
|
||||
Or perhaps there is an API variant using closures which may be useful in
|
||||
combination with some `Closure::from_*` helpers (see still same example).
|
||||
|
||||
|
||||
## SEE ALSO
|
||||
g-ir-scanner(1)
|
||||
159
cmake/external/glib/cppgir/examples/co-async.hpp
vendored
Normal file
159
cmake/external/glib/cppgir/examples/co-async.hpp
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
#pragma once
|
||||
|
||||
#include <gi/gi.hpp>
|
||||
|
||||
#include <coroutine>
|
||||
#include <future>
|
||||
|
||||
#ifdef CO_DEBUG
|
||||
#include <iostream>
|
||||
static auto &dout = std::cerr;
|
||||
#else
|
||||
#include <sstream>
|
||||
static std::ostringstream dout;
|
||||
#endif
|
||||
|
||||
template<typename T, typename SELF>
|
||||
struct holder
|
||||
{
|
||||
void return_value(T &&v)
|
||||
{
|
||||
dout << "return value " << std::endl;
|
||||
auto self = (SELF *)this;
|
||||
self->set_value(std::move(v));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename SELF>
|
||||
struct holder<void, SELF>
|
||||
{
|
||||
void return_void()
|
||||
{
|
||||
auto self = (SELF *)this;
|
||||
self->set_value();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RESULT>
|
||||
class promise_type_t : public holder<RESULT, promise_type_t<RESULT>>
|
||||
{
|
||||
protected:
|
||||
std::promise<RESULT> result_;
|
||||
std::coroutine_handle<> waiter_;
|
||||
|
||||
public:
|
||||
struct init
|
||||
{
|
||||
std::coroutine_handle<> handle;
|
||||
std::future<RESULT> f;
|
||||
};
|
||||
|
||||
~promise_type_t() { dout << "promise destruction" << std::endl; }
|
||||
|
||||
auto get_return_object(bool refresh = false)
|
||||
{
|
||||
dout << "return obj " << std::endl;
|
||||
if (refresh)
|
||||
result_ = decltype(result_)();
|
||||
return init{std::coroutine_handle<promise_type_t>::from_promise(*this),
|
||||
result_.get_future()};
|
||||
}
|
||||
std::suspend_never initial_suspend() noexcept { return {}; }
|
||||
std::suspend_never final_suspend() noexcept { return {}; }
|
||||
|
||||
bool resume()
|
||||
{
|
||||
auto w = waiter_;
|
||||
if (w) {
|
||||
// waiter takes care of itself again
|
||||
waiter_ = nullptr;
|
||||
w.resume();
|
||||
}
|
||||
return bool(w);
|
||||
}
|
||||
|
||||
// use any dummy type to avoid reference to void below
|
||||
using arg_type = typename std::conditional<std::is_same<RESULT, void>::value,
|
||||
std::nullptr_t, RESULT>::type;
|
||||
|
||||
void set_value(arg_type &&v)
|
||||
{
|
||||
result_.set_value(std::move(v));
|
||||
resume();
|
||||
}
|
||||
|
||||
void set_value()
|
||||
{
|
||||
result_.set_value();
|
||||
resume();
|
||||
}
|
||||
|
||||
void set_waiter(std::coroutine_handle<> handle)
|
||||
{
|
||||
// a task/promise represent a coroutine function (frame)
|
||||
// it should only be waited upon by one other task
|
||||
// (rather than handed around and waited in multiple locations)
|
||||
if (waiter_)
|
||||
gi::detail::try_throw(std::logic_error("already waited upon"));
|
||||
waiter_ = handle;
|
||||
}
|
||||
|
||||
void unhandled_exception()
|
||||
{
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
result_.set_exception(std::current_exception());
|
||||
// if no-one waiting, deliver to caller
|
||||
// the latter likely is the original caller
|
||||
// (to which we have not yet returned, so it can yet await)
|
||||
// otherwise it might end up totally lost
|
||||
if (!resume())
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RESULT, typename P = promise_type_t<RESULT>>
|
||||
class task
|
||||
{
|
||||
public:
|
||||
using promise_type = P;
|
||||
|
||||
// only 1 actually active
|
||||
std::coroutine_handle<promise_type> coro_;
|
||||
std::unique_ptr<promise_type> p_;
|
||||
// but always this
|
||||
std::future<RESULT> result_;
|
||||
|
||||
public:
|
||||
// NOTE if coroutine exits by co_return, then handle is not useful
|
||||
// but the future should have a value
|
||||
task(typename P::init i)
|
||||
: coro_(decltype(coro_)::from_address(i.handle.address())),
|
||||
result_(std::move(i.f))
|
||||
{
|
||||
dout << "init task" << std::endl;
|
||||
}
|
||||
|
||||
task() : p_(new promise_type()) { result_ = p_->get_return_object().f; }
|
||||
|
||||
// move-only
|
||||
task(task &&other) = default;
|
||||
task &operator=(task &&other) = delete;
|
||||
|
||||
bool await_ready()
|
||||
{
|
||||
return result_.wait_for(std::chrono::seconds(0)) ==
|
||||
std::future_status::ready;
|
||||
}
|
||||
|
||||
promise_type &promise() const { return coro_ ? coro_.promise() : *p_; }
|
||||
|
||||
void await_suspend(std::coroutine_handle<> handle)
|
||||
{
|
||||
if (!coro_ && !p_)
|
||||
gi::detail::try_throw(std::logic_error("no routine to wait on"));
|
||||
promise().set_waiter(handle);
|
||||
}
|
||||
|
||||
RESULT await_resume() { return result_.get(); }
|
||||
};
|
||||
74
cmake/external/glib/cppgir/examples/external/CMakeLists.txt
vendored
Normal file
74
cmake/external/glib/cppgir/examples/external/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR)
|
||||
project(cppgir_example VERSION 2.0.0)
|
||||
|
||||
# following example uses the Gio-2.0 gir
|
||||
# will generate code for that
|
||||
|
||||
# locate cppgir
|
||||
|
||||
# NOTE recent cmake version have more options and automagic find_package integration
|
||||
# (see https://cmake.org/cmake/help/latest/guide/using-dependencies/index.html
|
||||
# or https://cmake.org/cmake/help/latest/module/FetchContent.html)
|
||||
# but a slightly older approach is used here
|
||||
find_package(cppgir ${CMAKE_PROJECT_VERSION} QUIET)
|
||||
if (cppgir_FOUND)
|
||||
# ok
|
||||
message(STATUS "using system cppgir")
|
||||
elseif (CMAKE_VERSION GREATER_EQUAL 3.22)
|
||||
# older version fail processing the subdirectory below
|
||||
message(STATUS "cppgir not found, fetching instead")
|
||||
# of course, alternatively,
|
||||
# use any other method to include cppgir (e.g. git submodule)
|
||||
# which can also be done unconditionally (without trying find_package first)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
cppgir
|
||||
GIT_REPOSITORY https://gitlab.com/mnauw/cppgir.git
|
||||
GIT_TAG master
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_SUBMODULES_RECURSE TRUE
|
||||
# disable auto-invoke of add_subdirectory() below
|
||||
SOURCE_SUBDIR data
|
||||
)
|
||||
# no CMakeLists in specified subdir, so we handle it explicitly
|
||||
FetchContent_MakeAvailable(cppgir)
|
||||
set(CPPGIR_DIR "${cppgir_SOURCE_DIR}")
|
||||
else ()
|
||||
# obviously, actual mileage/location may vary (e.g. using git submodule)
|
||||
set(CPPGIR_DIR "../..")
|
||||
endif ()
|
||||
|
||||
if (CPPGIR_DIR)
|
||||
# adjust options
|
||||
set(BUILD_TESTING OFF)
|
||||
set(BUILD_EXAMPLES OFF)
|
||||
set(BUILD_EMBED_IGNORE ON)
|
||||
add_subdirectory(${CPPGIR_DIR} cppgir EXCLUDE_FROM_ALL)
|
||||
endif ()
|
||||
|
||||
set(EXAMPLE_NS "Gio-2.0")
|
||||
# of course, can be anywhere, e.g. inside/outside the build directory
|
||||
# set(GENERATED_DIR "/tmp/gi.example")
|
||||
set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
add_custom_command(OUTPUT ${GENERATED_DIR}
|
||||
COMMENT "Generating wrapper code for: ${EXAMPLE_NS}"
|
||||
DEPENDS CppGir::cppgir
|
||||
COMMAND CppGir::cppgir --output ${GENERATED_DIR} ${EXAMPLE_NS}
|
||||
COMMAND cmake -E touch_nocreate ${GENERATED_DIR}
|
||||
)
|
||||
add_custom_target(generate DEPENDS ${GENERATED_DIR})
|
||||
|
||||
# need the code and libs and especially GIRs
|
||||
include(FindPkgConfig)
|
||||
pkg_check_modules(GOBJECT REQUIRED IMPORTED_TARGET gobject-2.0)
|
||||
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0 gio-unix-2.0)
|
||||
|
||||
# (actually) needs no generated code, but it needs basic libs
|
||||
add_executable(ext-gobject src/ext-gobject.cpp)
|
||||
target_link_libraries(ext-gobject PRIVATE CppGir::gi PkgConfig::GOBJECT)
|
||||
|
||||
# (really) needs generated code
|
||||
add_executable(ext-gio src/ext-gio.cpp)
|
||||
target_link_libraries(ext-gio PRIVATE CppGir::gi PkgConfig::GIO)
|
||||
target_include_directories(ext-gio PRIVATE ${GENERATED_DIR})
|
||||
add_dependencies(ext-gio generate)
|
||||
6
cmake/external/glib/cppgir/examples/external/README.md
vendored
Normal file
6
cmake/external/glib/cppgir/examples/external/README.md
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# cppgir_example
|
||||
|
||||
This example project shows how `cppgir` can be used with `CMake` or `meson`.
|
||||
In either case, the required `cppgir` parts can either be found "in the system"
|
||||
(as previously installed by some package or build), or built as part of the
|
||||
project itself (by suitable form of inclusion).
|
||||
49
cmake/external/glib/cppgir/examples/external/conanfile.py
vendored
Normal file
49
cmake/external/glib/cppgir/examples/external/conanfile.py
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
import os
|
||||
|
||||
from conan import ConanFile
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
from conan.tools.build import can_run
|
||||
|
||||
|
||||
class CppGirExampleConan(ConanFile):
|
||||
version = "2.0.0"
|
||||
name = "cppgir-example"
|
||||
description = "cppgir example"
|
||||
license = "MIT Software License"
|
||||
url = "https://gitlab.com/mnauw/cppgir.git"
|
||||
exports_sources = "src/*", "CMakeLists.txt"
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
build_policy = "missing"
|
||||
author = "Mark Nauwelaerts"
|
||||
|
||||
def requirements(self):
|
||||
req = self.tested_reference_str
|
||||
if req is None:
|
||||
req = "cppgir/" + self.version
|
||||
self.requires(req)
|
||||
# also requires GLib
|
||||
# however, no GIRs in conan binary package
|
||||
# which nowadays requires a bootstrap dance with gobject-introspection
|
||||
# so rely on a distro package instead
|
||||
# self.requires("glib/2.78.3")
|
||||
|
||||
def build_requirements(self):
|
||||
self.tool_requires("cppgir/<host_version>",
|
||||
options={'header_only': False})
|
||||
|
||||
def layout(self):
|
||||
cmake_layout(self, build_folder="build.conan")
|
||||
|
||||
def generate(self):
|
||||
tc = CMakeToolchain(self)
|
||||
tc.generate()
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure()
|
||||
cmake.build()
|
||||
|
||||
def test(self):
|
||||
if can_run(self):
|
||||
cmd = os.path.join(self.cpp.build.bindir, "ext-gobject")
|
||||
self.run(cmd, env="conanrun")
|
||||
57
cmake/external/glib/cppgir/examples/external/meson.build
vendored
Normal file
57
cmake/external/glib/cppgir/examples/external/meson.build
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
project(
|
||||
'cppgir_example',
|
||||
['c', 'cpp'],
|
||||
version: '2.0.0',
|
||||
meson_version: '>= 0.61',
|
||||
default_options: ['warning_level=2', 'cpp_std=c++17'],
|
||||
)
|
||||
|
||||
# following example uses the Gio-2.0 gir
|
||||
# will generate code for that
|
||||
|
||||
# locate cppgir, either in system or subproject
|
||||
# if subproject, arrange to embed ignore data
|
||||
# (since it will run un-installed, so data can not be found in installed location)
|
||||
gi = dependency(
|
||||
'cppgir',
|
||||
required: true,
|
||||
version: '>=2.0.0',
|
||||
fallback: 'cppgir',
|
||||
default_options: ['build_embed_ignore=true'],
|
||||
)
|
||||
cppgir = find_program('cppgir', required: true)
|
||||
|
||||
generated_src = custom_target(
|
||||
'src',
|
||||
# actually it generates all this (and more)
|
||||
# output : ['gio.cpp', 'gio.hpp', 'glib.cpp', 'glib.hpp', 'gobject.cpp', 'gobject.hpp'],
|
||||
# but the example below will inline all of the above, so no source files are used
|
||||
# (though it is recommended for a real case, rather than a full inline)
|
||||
output: ['gio.hpp'],
|
||||
console: true,
|
||||
# output-top creates extra files in top-level (as oppposed to subdir)
|
||||
# since "output" paths above "must not contain a path segment" (says meson, sigh)
|
||||
command: [cppgir, '--output-top', '--output', '@OUTDIR@', 'Gio-2.0'],
|
||||
)
|
||||
|
||||
# need the code and libs and especially GIRs
|
||||
gobject = dependency('gobject-2.0', required: true)
|
||||
gio = dependency('gio-2.0', required: true)
|
||||
# the Gio GIR data also covers this, so generated code references it
|
||||
gio_unix = dependency('gio-unix-2.0', required: true)
|
||||
|
||||
deps = [gi, gobject, gio, gio_unix]
|
||||
|
||||
# (actually) needs no generated code, but it needs basic libs
|
||||
ex_gobject = executable(
|
||||
'ext-gobject',
|
||||
['src/ext-gobject.cpp'],
|
||||
dependencies: deps,
|
||||
)
|
||||
|
||||
# (really) needs generated code
|
||||
ex_gio = executable(
|
||||
'ext-gio',
|
||||
['src/ext-gio.cpp', generated_src],
|
||||
dependencies: deps,
|
||||
)
|
||||
1
cmake/external/glib/cppgir/examples/external/src/ext-gio.cpp
vendored
Normal file
1
cmake/external/glib/cppgir/examples/external/src/ext-gio.cpp
vendored
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../gio.cpp"
|
||||
1
cmake/external/glib/cppgir/examples/external/src/ext-gobject.cpp
vendored
Normal file
1
cmake/external/glib/cppgir/examples/external/src/ext-gobject.cpp
vendored
Normal file
@@ -0,0 +1 @@
|
||||
#include "../../gobject.cpp"
|
||||
5
cmake/external/glib/cppgir/examples/external/subprojects/cppgir.wrap
vendored
Normal file
5
cmake/external/glib/cppgir/examples/external/subprojects/cppgir.wrap
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://gitlab.com/mnauw/cppgir.git
|
||||
revision = master
|
||||
clone-recursive = true
|
||||
depth = 1
|
||||
254
cmake/external/glib/cppgir/examples/gio-async-co.cpp
vendored
Normal file
254
cmake/external/glib/cppgir/examples/gio-async-co.cpp
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
#define GI_INLINE 1
|
||||
#include <gio/gio.hpp>
|
||||
|
||||
#include "co-async.hpp"
|
||||
|
||||
namespace GLib = gi::repository::GLib;
|
||||
namespace GObject_ = gi::repository::GObject;
|
||||
namespace Gio = gi::repository::Gio;
|
||||
|
||||
template<typename R>
|
||||
class async_result_promise_type_t : public promise_type_t<R>
|
||||
{
|
||||
public:
|
||||
Gio::Cancellable cancel_;
|
||||
};
|
||||
|
||||
template<typename RESULT = Gio::AsyncResult>
|
||||
class async_result : public task<RESULT, async_result_promise_type_t<RESULT>>
|
||||
{
|
||||
public:
|
||||
using super_type = task<RESULT, async_result_promise_type_t<RESULT>>;
|
||||
|
||||
using super_type::super_type;
|
||||
|
||||
using super_type::await_suspend;
|
||||
|
||||
// NOTE; in general, if await_ready == false, then there is no result yet,
|
||||
// so the handle's frame should not have co_return'ed yet
|
||||
// so the handle/promise should still be valid
|
||||
|
||||
template<typename OR>
|
||||
void await_suspend(
|
||||
std::coroutine_handle<async_result_promise_type_t<OR>> handle)
|
||||
{
|
||||
// propagate cancellable
|
||||
// the handle should be valid
|
||||
// (as await_ready == false, otherwise no suspend should happen)
|
||||
handle.promise().cancel_ = this->promise().cancel_;
|
||||
// usual suspend
|
||||
super_type::await_suspend(handle);
|
||||
}
|
||||
|
||||
operator Gio::AsyncReadyCallback()
|
||||
{
|
||||
// only makes sense in default case
|
||||
static_assert(std::is_same<RESULT, Gio::AsyncResult>::value, "");
|
||||
if (this->p_) {
|
||||
// setup for new gio call
|
||||
this->result_ = this->p_->get_return_object(true).f;
|
||||
// also arrange for new cancellable below
|
||||
this->p_->cancel_ = nullptr;
|
||||
return [this](GObject_::Object, Gio::AsyncResult result) {
|
||||
this->promise().return_value(std::move(result));
|
||||
};
|
||||
} else {
|
||||
// so this task is the result of a coroutine frame
|
||||
// then it should be completed by the latter, not a Gio callback
|
||||
g_warning("no callback to complete coroutine");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Gio::Cancellable cancellable()
|
||||
{
|
||||
Gio::Cancellable ret;
|
||||
if (this->p_) {
|
||||
if (!this->p_->cancel_)
|
||||
this->p_->cancel_ = Gio::Cancellable::new_();
|
||||
ret = this->p_->cancel_;
|
||||
} else if (!this->await_ready()) {
|
||||
// no create here, only propagate
|
||||
ret = this->promise().cancel_;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Gio::Cancellable timeout(
|
||||
const std::chrono::milliseconds &to, Gio::Cancellable cancel)
|
||||
{
|
||||
if (to.count() > 0 && cancel) {
|
||||
GLib::timeout_add_once(
|
||||
to.count(), [cancel]() mutable { cancel.cancel(); });
|
||||
}
|
||||
return cancel;
|
||||
}
|
||||
};
|
||||
|
||||
async_result<void>
|
||||
sleep_for(std::chrono::milliseconds to)
|
||||
{
|
||||
async_result<void> w;
|
||||
GLib::timeout_add_once(to.count(), [&w] { w.promise().return_void(); });
|
||||
co_await w;
|
||||
co_return;
|
||||
}
|
||||
|
||||
static task<void>
|
||||
async_client(int port, int id, int &count)
|
||||
{
|
||||
async_result w;
|
||||
|
||||
auto dest = Gio::NetworkAddress::new_loopback(port);
|
||||
|
||||
std::string sid = "client ";
|
||||
sid += std::to_string(id);
|
||||
|
||||
// connect a client
|
||||
std::cout << sid << ": connect" << std::endl;
|
||||
auto client = Gio::SocketClient::new_();
|
||||
client.connect_async(dest, w);
|
||||
auto conn = gi::expect(client.connect_finish(co_await w));
|
||||
|
||||
// say something
|
||||
auto os = conn.get_output_stream();
|
||||
std::cout << sid << ": send: " << sid << std::endl;
|
||||
os.write_all_async(
|
||||
(guint8 *)sid.data(), sid.size(), GLib::PRIORITY_DEFAULT_, w);
|
||||
os.write_all_finish(co_await w, (gsize *)nullptr);
|
||||
|
||||
// now hear what the other side has to say
|
||||
std::cout << sid << ": receive" << std::endl;
|
||||
auto is = conn.get_input_stream();
|
||||
while (1) {
|
||||
guint8 data[1024];
|
||||
is.read_async(data, sizeof(data), GLib::PRIORITY_DEFAULT_, w);
|
||||
auto size = gi::expect(is.read_finish(co_await w));
|
||||
if (!size)
|
||||
break;
|
||||
std::string msg(data, data + size);
|
||||
std::cout << sid << ": got data: " << msg << std::endl;
|
||||
}
|
||||
|
||||
std::cout << sid << ": closing down" << std::endl;
|
||||
--count;
|
||||
}
|
||||
|
||||
static task<void>
|
||||
async_handle_client(Gio::SocketConnection conn)
|
||||
{
|
||||
async_result w;
|
||||
|
||||
// say hello
|
||||
auto os = conn.get_output_stream();
|
||||
std::string msg = "hello ";
|
||||
os.write_all_async(
|
||||
(guint8 *)msg.data(), msg.size(), GLib::PRIORITY_DEFAULT_, w);
|
||||
os.write_all_finish(co_await w, (gsize *)nullptr);
|
||||
|
||||
// now echo what the other side has to say
|
||||
auto is = conn.get_input_stream();
|
||||
while (1) {
|
||||
guint8 data[1024];
|
||||
// give up if timeout
|
||||
GLib::Error error;
|
||||
is.read_async(data, sizeof(data), GLib::PRIORITY_DEFAULT_,
|
||||
w.timeout(std::chrono::milliseconds(200), w.cancellable()), w);
|
||||
auto size = gi::expect(is.read_finish(co_await w, &error));
|
||||
if (error) {
|
||||
if (error.matches(G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||||
break;
|
||||
} else {
|
||||
gi::detail::try_throw(std::move(error));
|
||||
}
|
||||
}
|
||||
std::string msg(data, data + size);
|
||||
std::cout << "server: got data: " << msg << std::endl;
|
||||
os.write_all_async(data, size, GLib::PRIORITY_DEFAULT_, w);
|
||||
os.write_all_finish(co_await w, (gsize *)nullptr);
|
||||
}
|
||||
|
||||
std::cout << "server: closing down client" << std::endl;
|
||||
}
|
||||
|
||||
static task<void>
|
||||
async_server(int clients, int &port)
|
||||
{
|
||||
async_result w;
|
||||
|
||||
auto listener = Gio::SocketListener::new_();
|
||||
port = gi::expect(listener.add_any_inet_port());
|
||||
|
||||
int count = 0;
|
||||
while (count < clients) {
|
||||
// accept clients
|
||||
std::cout << "server: accepting" << std::endl;
|
||||
listener.accept_async(w);
|
||||
auto conn = gi::expect(
|
||||
listener.accept_finish(co_await w, (GObject_::Object *)nullptr));
|
||||
|
||||
// spawn client handler
|
||||
std::cout << "server: new connection" << std::endl;
|
||||
// task will run itself to completion, no need to wait/watch it here
|
||||
async_handle_client(conn);
|
||||
++count;
|
||||
}
|
||||
|
||||
// wait a bit more and shutdown, because we can
|
||||
co_await sleep_for(std::chrono::milliseconds(1000));
|
||||
}
|
||||
|
||||
void
|
||||
async_demo(GLib::MainLoop loop, int clients)
|
||||
{
|
||||
// run server
|
||||
// dispatch at once to obtain port
|
||||
int port = 0;
|
||||
auto server = async_server(clients, port);
|
||||
|
||||
// make clients
|
||||
int count = 0;
|
||||
for (int i = 0; i < clients; ++i) {
|
||||
++count;
|
||||
// client task runs to completion, no need to wait/watch
|
||||
// NOTE this frame will stay alive, so count ref is valid
|
||||
async_client(port, i, count);
|
||||
}
|
||||
|
||||
// plain-and-simple; poll regularly and quit when all clients done
|
||||
task<void> cd;
|
||||
auto check = [&]() -> gboolean {
|
||||
if (!count) {
|
||||
cd.promise().return_void();
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
return G_SOURCE_CONTINUE;
|
||||
};
|
||||
GLib::timeout_add(100, check);
|
||||
|
||||
auto wait = [&]() -> task<void> {
|
||||
// wait clients
|
||||
co_await cd;
|
||||
// server should also have completed
|
||||
co_await server;
|
||||
std::cout << "server down" << std::endl;
|
||||
loop.quit();
|
||||
};
|
||||
|
||||
wait();
|
||||
|
||||
std::cout << "running loop" << std::endl;
|
||||
loop.run();
|
||||
std::cout << "ending loop" << std::endl;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
auto loop = GLib::MainLoop::new_();
|
||||
|
||||
int clients = argc > 1 ? std::stoi(argv[1]) : 0;
|
||||
std::cout << clients << " clients" << std::endl;
|
||||
if (clients > 0)
|
||||
async_demo(loop, clients);
|
||||
}
|
||||
351
cmake/external/glib/cppgir/examples/gio-async.cpp
vendored
Normal file
351
cmake/external/glib/cppgir/examples/gio-async.cpp
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
#define GI_INLINE 1
|
||||
#include <gio/gio.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
namespace GLib = gi::repository::GLib;
|
||||
namespace GObject_ = gi::repository::GObject;
|
||||
namespace Gio = gi::repository::Gio;
|
||||
|
||||
class context_scheduler : public boost::fibers::algo::round_robin
|
||||
{
|
||||
typedef context_scheduler self;
|
||||
typedef boost::fibers::algo::round_robin super;
|
||||
|
||||
struct src : GSource
|
||||
{
|
||||
self *scheduler;
|
||||
};
|
||||
|
||||
GSourceFuncs funcs{};
|
||||
GLib::MainContext ctx_;
|
||||
src *source_;
|
||||
boost::fibers::condition_variable cond_;
|
||||
boost::fibers::mutex mtx_;
|
||||
using clock_type = std::chrono::steady_clock;
|
||||
bool dispatching_ = false;
|
||||
|
||||
static gboolean src_dispatch(
|
||||
GSource *source, GSourceFunc /*callback*/, gpointer /*user_data*/)
|
||||
{
|
||||
auto s = (src *)(source);
|
||||
auto sched = s->scheduler;
|
||||
// wait here to give (other) fibers a chance
|
||||
sched->dispatching_ = true;
|
||||
std::unique_lock<boost::fibers::mutex> lk(sched->mtx_);
|
||||
sched->cond_.wait(lk);
|
||||
sched->dispatching_ = false;
|
||||
// all available work has been done while we were waiting above
|
||||
// no need to dispatch again until new work
|
||||
// which we accept as of now (due to mainloop activity)
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
public:
|
||||
context_scheduler(GLib::MainContext ctx) : ctx_(ctx)
|
||||
{
|
||||
// this is a bit too much for bindings, so handle the raw C way
|
||||
funcs.dispatch = src_dispatch;
|
||||
auto s = g_source_new(&funcs, sizeof(src));
|
||||
source_ = (src *)(s);
|
||||
source_->scheduler = this;
|
||||
g_source_attach(s, ctx.gobj_());
|
||||
}
|
||||
|
||||
~context_scheduler()
|
||||
{
|
||||
g_source_destroy(source_);
|
||||
g_source_unref(source_);
|
||||
}
|
||||
|
||||
void awakened(boost::fibers::context *t) noexcept override
|
||||
{
|
||||
// delegate first
|
||||
super::awakened(t);
|
||||
// arrange for dispatch of work
|
||||
// discard awake of source dispatch
|
||||
if (!dispatching_)
|
||||
g_source_set_ready_time(source_, 0);
|
||||
}
|
||||
|
||||
void suspend_until(
|
||||
std::chrono::steady_clock::time_point const &abs_time) noexcept override
|
||||
{
|
||||
// release dispatch
|
||||
// should only end up here while dispatching in source
|
||||
// (rather than inadvertently trying to block main loop,
|
||||
// which would then lead to busy loop)
|
||||
if (dispatching_) {
|
||||
// derive time of subsequent dispatch
|
||||
if (clock_type::time_point::max() != abs_time) {
|
||||
auto to = abs_time - std::chrono::steady_clock::now();
|
||||
int ms =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(to).count();
|
||||
ms = std::max(ms, 0);
|
||||
g_source_set_ready_time(source_, g_get_monotonic_time() + ms);
|
||||
} else {
|
||||
g_source_set_ready_time(source_, -1);
|
||||
}
|
||||
// release source dispatching
|
||||
cond_.notify_one();
|
||||
} else {
|
||||
// suspend is requested, so there is nothing to do for a while
|
||||
// so in particular the main fiber is then also blocked (e.g. some sleep)
|
||||
// such main loop blocking is also/still not allowed
|
||||
// (if such is active)
|
||||
g_assert(g_main_depth() == 0);
|
||||
// no running loop (so also no dispatch)
|
||||
// so delegate to the usual scheduling
|
||||
// (which will really block the hard way, rather than poll)
|
||||
super::suspend_until(abs_time);
|
||||
}
|
||||
}
|
||||
|
||||
// might be called from a different thread
|
||||
void notify() noexcept override
|
||||
{
|
||||
// discard our own notify above to resume source dispatch
|
||||
if (dispatching_)
|
||||
return;
|
||||
ctx_.wakeup();
|
||||
}
|
||||
};
|
||||
|
||||
class async_future
|
||||
{
|
||||
boost::fibers::promise<Gio::AsyncResult> p_;
|
||||
boost::fibers::future<Gio::AsyncResult> f_;
|
||||
Gio::Cancellable cancel_;
|
||||
GLib::Source timeout_;
|
||||
|
||||
public:
|
||||
~async_future()
|
||||
{
|
||||
if (timeout_)
|
||||
timeout_.destroy();
|
||||
}
|
||||
|
||||
operator Gio::AsyncReadyCallback()
|
||||
{
|
||||
// prepare a new promise
|
||||
p_ = decltype(p_)();
|
||||
f_ = p_.get_future();
|
||||
cancel_ = nullptr;
|
||||
return [&](GObject_::Object, Gio::AsyncResult result) {
|
||||
p_.set_value(result);
|
||||
};
|
||||
}
|
||||
|
||||
Gio::AsyncResult get() { return f_.get(); }
|
||||
|
||||
Gio::Cancellable cancellable()
|
||||
{
|
||||
if (!cancel_)
|
||||
cancel_ = Gio::Cancellable::new_();
|
||||
return cancel_;
|
||||
}
|
||||
|
||||
Gio::Cancellable timeout(const std::chrono::milliseconds &to)
|
||||
{
|
||||
auto cancel = cancellable();
|
||||
if (to.count() > 0) {
|
||||
timeout_ = GLib::timeout_source_new(to.count());
|
||||
auto do_timeout = [cancel]() mutable {
|
||||
cancel.cancel();
|
||||
return GLib::SOURCE_REMOVE_;
|
||||
};
|
||||
timeout_.set_callback<GLib::SourceFunc>(do_timeout);
|
||||
timeout_.attach(GLib::MainContext::get_thread_default());
|
||||
}
|
||||
return cancel_;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
async_client(int port, int id, int &count)
|
||||
{
|
||||
async_future w;
|
||||
|
||||
auto dest = Gio::NetworkAddress::new_loopback(port);
|
||||
|
||||
std::string sid = "client ";
|
||||
sid += std::to_string(id);
|
||||
|
||||
// connect a client
|
||||
std::cout << sid << ": connect" << std::endl;
|
||||
auto client = Gio::SocketClient::new_();
|
||||
client.connect_async(dest, w);
|
||||
auto conn = gi::expect(client.connect_finish(w.get()));
|
||||
|
||||
// say something
|
||||
auto os = conn.get_output_stream();
|
||||
std::cout << sid << ": send: " << sid << std::endl;
|
||||
os.write_all_async(
|
||||
(guint8 *)sid.data(), sid.size(), GLib::PRIORITY_DEFAULT_, w);
|
||||
os.write_all_finish(w.get(), (gsize *)nullptr);
|
||||
|
||||
// now hear what the other side has to say
|
||||
std::cout << sid << ": receive" << std::endl;
|
||||
auto is = conn.get_input_stream();
|
||||
while (1) {
|
||||
guint8 data[1024];
|
||||
is.read_async(data, sizeof(data), GLib::PRIORITY_DEFAULT_, w);
|
||||
auto size = gi::expect(is.read_finish(w.get()));
|
||||
if (!size)
|
||||
break;
|
||||
std::string msg(data, data + size);
|
||||
std::cout << sid << ": got data: " << msg << std::endl;
|
||||
}
|
||||
|
||||
std::cout << sid << ": closing down" << std::endl;
|
||||
--count;
|
||||
}
|
||||
|
||||
static void
|
||||
async_handle_client(Gio::SocketConnection conn)
|
||||
{
|
||||
async_future w;
|
||||
|
||||
// say hello
|
||||
auto os = conn.get_output_stream();
|
||||
std::string msg = "hello ";
|
||||
os.write_all_async(
|
||||
(guint8 *)msg.data(), msg.size(), GLib::PRIORITY_DEFAULT_, w);
|
||||
os.write_all_finish(w.get(), (gsize *)nullptr);
|
||||
|
||||
// now echo what the other side has to say
|
||||
auto is = conn.get_input_stream();
|
||||
while (1) {
|
||||
guint8 data[1024];
|
||||
// give up if timeout
|
||||
GLib::Error error;
|
||||
is.read_async(data, sizeof(data), GLib::PRIORITY_DEFAULT_,
|
||||
w.timeout(std::chrono::milliseconds(200)), w);
|
||||
auto size = gi::expect(is.read_finish(w.get(), &error));
|
||||
if (error) {
|
||||
if (error.matches(G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||||
break;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
std::string msg(data, data + size);
|
||||
std::cout << "server: got data: " << msg << std::endl;
|
||||
os.write_all_async(data, size, GLib::PRIORITY_DEFAULT_, w);
|
||||
os.write_all_finish(w.get(), (gsize *)nullptr);
|
||||
}
|
||||
|
||||
std::cout << "server: closing down client" << std::endl;
|
||||
}
|
||||
|
||||
static void
|
||||
async_server(int clients, int &port)
|
||||
{
|
||||
async_future w;
|
||||
|
||||
auto listener = Gio::SocketListener::new_();
|
||||
port = gi::expect(listener.add_any_inet_port());
|
||||
|
||||
int count = 0;
|
||||
while (count < clients) {
|
||||
// accept clients
|
||||
std::cout << "server: accepting" << std::endl;
|
||||
listener.accept_async(w);
|
||||
auto conn = gi::expect(
|
||||
listener.accept_finish(w.get(), (GObject_::Object *)nullptr));
|
||||
|
||||
// spawn client handler
|
||||
std::cout << "server: new connection" << std::endl;
|
||||
boost::fibers::fiber c(async_handle_client, conn);
|
||||
c.detach();
|
||||
++count;
|
||||
}
|
||||
|
||||
// wait a bit and shutdown
|
||||
// wait long enough to test the out-of-loop join below
|
||||
boost::this_fiber::sleep_for(std::chrono::milliseconds(1000));
|
||||
std::cout << "server: shutdown" << std::endl;
|
||||
}
|
||||
|
||||
static void
|
||||
async_demo(GLib::MainLoop loop, int clients)
|
||||
{
|
||||
// run server
|
||||
// dispatch at once to obtain port
|
||||
int port = 0;
|
||||
boost::fibers::fiber server(
|
||||
boost::fibers::launch::dispatch, async_server, clients, std::ref(port));
|
||||
|
||||
// make clients
|
||||
int count = 0;
|
||||
for (int i = 0; i < clients; ++i) {
|
||||
++count;
|
||||
auto c = boost::fibers::fiber(async_client, port, i, std::ref(count));
|
||||
c.detach();
|
||||
}
|
||||
|
||||
// plain-and-simple; poll regularly and quit when all clients done
|
||||
auto check = [&]() {
|
||||
if (!count)
|
||||
loop.quit();
|
||||
return G_SOURCE_CONTINUE;
|
||||
};
|
||||
GLib::timeout_add(100, check);
|
||||
|
||||
std::cout << "running loop" << std::endl;
|
||||
loop.run();
|
||||
std::cout << "ending loop" << std::endl;
|
||||
|
||||
server.join();
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
GLib::MainLoop loop = GLib::MainLoop::new_();
|
||||
auto ctx = GLib::MainContext::default_();
|
||||
boost::fibers::use_scheduling_algorithm<context_scheduler>(ctx);
|
||||
|
||||
{ // basic fiber demo
|
||||
int count = 0;
|
||||
auto work = [&](const std::string &msg) {
|
||||
std::cout << msg << std::endl;
|
||||
++count;
|
||||
};
|
||||
auto quit = [&](int limit, const std::chrono::milliseconds &d) {
|
||||
while (count < limit)
|
||||
boost::this_fiber::sleep_for(d);
|
||||
loop.quit();
|
||||
};
|
||||
boost::fibers::fiber f1(work, "fiber 1");
|
||||
boost::fibers::fiber f2(work, "fiber 2");
|
||||
boost::fibers::fiber q(quit, 2, std::chrono::milliseconds(100));
|
||||
|
||||
loop.run();
|
||||
|
||||
f1.join();
|
||||
f2.join();
|
||||
q.join();
|
||||
}
|
||||
|
||||
// now an optional async GIO demo
|
||||
int clients = argc > 1 ? std::stoi(argv[1]) : 0;
|
||||
std::cout << clients << " clients" << std::endl;
|
||||
if (clients > 0)
|
||||
async_demo(loop, clients);
|
||||
}
|
||||
|
||||
#else
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
std::cerr << "boost::fibers needs exceptions";
|
||||
}
|
||||
|
||||
#endif
|
||||
63
cmake/external/glib/cppgir/examples/gio-dbus-client.cpp
vendored
Normal file
63
cmake/external/glib/cppgir/examples/gio-dbus-client.cpp
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
#define GI_INLINE 1
|
||||
#include <gio/gio.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace GObject_ = gi::repository::GObject;
|
||||
namespace GLib = gi::repository::GLib;
|
||||
namespace Gio = gi::repository::Gio;
|
||||
|
||||
static GLib::MainLoop loop;
|
||||
|
||||
// many calls here support GError
|
||||
// so typically will throw here instead
|
||||
// (unless GError output is explicitly requested in call signature)
|
||||
|
||||
// NOTE abundancy of gi::expect not generally needed;
|
||||
// only needed when using --dl and --expected
|
||||
|
||||
static void
|
||||
on_reply(GObject_::Object ob, Gio::AsyncResult result)
|
||||
{
|
||||
// if not caught here, it will be caught before returning to plain C
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
auto connection = gi::object_cast<Gio::DBusConnection>(ob);
|
||||
|
||||
auto call_result = gi::expect(connection.call_finish(result));
|
||||
// get single array child
|
||||
auto names = gi::expect(call_result.get_child_value(0));
|
||||
int count = gi::expect(names.n_children());
|
||||
|
||||
std::cout << count << " message bus names: " << std::endl;
|
||||
for (int i = 0; i < count; ++i)
|
||||
std::cout << gi::expect(
|
||||
gi::expect(names.get_child_value(i)).get_string(nullptr))
|
||||
<< std::endl;
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
} catch (const GLib::Error &error) {
|
||||
std::cerr << "error: '" << error.what() << "'." << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
// quit when idle
|
||||
GLib::idle_add([]() {
|
||||
loop.quit();
|
||||
return GLib::SOURCE_REMOVE_;
|
||||
});
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char ** /*argv*/)
|
||||
{
|
||||
auto bustype = argc <= 1 ? Gio::BusType::SESSION_ : Gio::BusType::SYSTEM_;
|
||||
auto connection = gi::expect(Gio::bus_get_sync(bustype));
|
||||
|
||||
connection.call("org.freedesktop.DBus", "/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus", "ListNames", nullptr, nullptr,
|
||||
Gio::DBusCallFlags::NONE_, -1, nullptr, on_reply);
|
||||
|
||||
loop = gi::expect(GLib::MainLoop::new_());
|
||||
loop.run();
|
||||
}
|
||||
225
cmake/external/glib/cppgir/examples/gio-qt-async.cpp
vendored
Normal file
225
cmake/external/glib/cppgir/examples/gio-qt-async.cpp
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
#define GI_INLINE 1
|
||||
#include <gio/gio.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QObject>
|
||||
|
||||
namespace GLib = gi::repository::GLib;
|
||||
namespace GObject_ = gi::repository::GObject;
|
||||
namespace Gio = gi::repository::Gio;
|
||||
|
||||
template<typename T>
|
||||
using ResultExtractor = std::function<void(
|
||||
GObject_::Object, Gio::AsyncResult result, QFutureInterface<T> &fut)>;
|
||||
|
||||
template<typename T>
|
||||
class qt_future
|
||||
{
|
||||
struct CallbackData
|
||||
{
|
||||
QFutureInterface<T> promise;
|
||||
QFutureWatcher<T> watcher;
|
||||
Gio::Cancellable cancel;
|
||||
ResultExtractor<T> handler;
|
||||
};
|
||||
|
||||
std::shared_ptr<CallbackData> data_;
|
||||
|
||||
public:
|
||||
qt_future(ResultExtractor<T> h)
|
||||
{
|
||||
data_ = std::make_shared<CallbackData>();
|
||||
data_->handler = h;
|
||||
}
|
||||
|
||||
QFuture<T> future() { return data_->promise.future(); }
|
||||
|
||||
QFutureInterface<T> promise() { return data_->promise; }
|
||||
|
||||
operator Gio::AsyncReadyCallback()
|
||||
{
|
||||
auto d = data_;
|
||||
return [d](GObject_::Object obj, Gio::AsyncResult result) {
|
||||
assert(d->handler);
|
||||
d->handler(obj, result, d->promise);
|
||||
};
|
||||
}
|
||||
|
||||
Gio::Cancellable cancellable()
|
||||
{
|
||||
if (!data_->cancel) {
|
||||
auto cancel = data_->cancel = Gio::Cancellable::new_();
|
||||
data_->watcher.setFuture(future());
|
||||
auto h = [cancel]() mutable { cancel.cancel(); };
|
||||
data_->watcher.connect(
|
||||
&data_->watcher, &decltype(data_->watcher)::finished, std::move(h));
|
||||
}
|
||||
return data_->cancel;
|
||||
}
|
||||
|
||||
// why not ... ??
|
||||
operator Gio::Cancellable() { return cancellable(); }
|
||||
};
|
||||
|
||||
#if GI_CONST_METHOD
|
||||
#define CONST_METHOD const
|
||||
#else
|
||||
#define CONST_METHOD
|
||||
#endif
|
||||
|
||||
template<typename Result, typename Object>
|
||||
qt_future<Result>
|
||||
make_future(Result (Object::*mf)(Gio::AsyncResult, GLib::Error *) CONST_METHOD)
|
||||
{
|
||||
ResultExtractor<Result> f;
|
||||
f = [mf](GObject_::Object cbobj, Gio::AsyncResult result,
|
||||
QFutureInterface<Result> &fut) mutable {
|
||||
auto obj = gi::object_cast<Object>(cbobj);
|
||||
GLib::Error error{};
|
||||
auto res = (obj.*mf)(result, &error);
|
||||
if (error.gobj_()) {
|
||||
// FIXME use std::expected<Result> as a result type ??
|
||||
// reportException might be an option, if enabled, but leads to throw
|
||||
fut.reportFinished();
|
||||
qWarning() << "error code " << error.code_();
|
||||
qWarning() << "error message "
|
||||
<< QString::fromUtf8(error.message_().c_str());
|
||||
} else {
|
||||
fut.reportFinished(&res);
|
||||
}
|
||||
};
|
||||
return {f};
|
||||
}
|
||||
|
||||
// ====
|
||||
// some small future-centric API wrappers using above helper class
|
||||
|
||||
QFuture<Gio::FileInfo>
|
||||
file_query_file_system_info(
|
||||
Gio::File f, const std::string &attributes, gint io_priority)
|
||||
{
|
||||
auto fut = make_future(&Gio::File::query_filesystem_info_finish);
|
||||
f.query_filesystem_info_async(attributes, io_priority, fut, fut);
|
||||
return fut.future();
|
||||
}
|
||||
|
||||
QFuture<bool>
|
||||
file_copy(Gio::File src, Gio::File destination, Gio::FileCopyFlags flags,
|
||||
gint io_priority)
|
||||
{
|
||||
auto fut = make_future(&Gio::File::copy_finish);
|
||||
auto promise = fut.promise();
|
||||
auto p = [promise](
|
||||
goffset current_num_bytes, goffset total_num_bytes) mutable {
|
||||
promise.setProgressRange(0, total_num_bytes);
|
||||
promise.setProgressValue(current_num_bytes);
|
||||
};
|
||||
src.copy_async(destination, flags, io_priority, fut, p, fut);
|
||||
return fut.future();
|
||||
}
|
||||
|
||||
// ====
|
||||
|
||||
// save on typing elsewhere
|
||||
// also makes it movable in a way
|
||||
template<typename T>
|
||||
std::shared_ptr<QFutureWatcher<T>>
|
||||
make_watcher(QFuture<T> fut)
|
||||
{
|
||||
auto watcher = std::make_shared<QFutureWatcher<T>>();
|
||||
watcher->setFuture(fut);
|
||||
return watcher;
|
||||
}
|
||||
|
||||
// type-erased owner/cleanup type
|
||||
using Retainer = std::shared_ptr<std::nullptr_t>;
|
||||
|
||||
Retainer
|
||||
do_query(QCoreApplication &app, const std::string &fpath)
|
||||
{
|
||||
auto file = Gio::File::new_for_commandline_arg(fpath);
|
||||
auto fut = file_query_file_system_info(file, "*", GLib::PRIORITY_DEFAULT_);
|
||||
auto watcher = make_watcher(fut);
|
||||
auto h = [fut, &app]() {
|
||||
Q_ASSERT(fut.isFinished());
|
||||
std::cout << "query finished" << std::endl;
|
||||
if (fut.resultCount()) {
|
||||
auto finfo = fut.result();
|
||||
for (auto attr : {Gio::FILE_ATTRIBUTE_FILESYSTEM_TYPE_,
|
||||
Gio::FILE_ATTRIBUTE_FILESYSTEM_FREE_,
|
||||
Gio::FILE_ATTRIBUTE_FILESYSTEM_SIZE_}) {
|
||||
std::cout << attr << ": " << finfo.get_attribute_as_string(attr)
|
||||
<< std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "... but no results" << std::endl;
|
||||
}
|
||||
app.quit();
|
||||
};
|
||||
watcher->connect(
|
||||
watcher.get(), &decltype(watcher)::element_type::finished, &app, h);
|
||||
return {watcher, nullptr};
|
||||
}
|
||||
|
||||
Retainer
|
||||
do_copy(QCoreApplication &app, const std::string &src, const std::string &dest)
|
||||
{
|
||||
auto fsrc = Gio::File::new_for_commandline_arg(src);
|
||||
auto fdest = Gio::File::new_for_commandline_arg(dest);
|
||||
auto fut = file_copy(
|
||||
fsrc, fdest, Gio::FileCopyFlags::ALL_METADATA_, GLib::PRIORITY_DEFAULT_);
|
||||
auto watcher = make_watcher(fut);
|
||||
auto h = [fut, &app]() {
|
||||
Q_ASSERT(fut.isFinished());
|
||||
std::cout << "copy finished" << std::endl;
|
||||
if (fut.resultCount()) {
|
||||
auto ok = fut.result();
|
||||
if (ok) {
|
||||
std::cout << "copy ok" << std::endl;
|
||||
} else {
|
||||
std::cout << "copy failed" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "... but no results" << std::endl;
|
||||
}
|
||||
app.quit();
|
||||
};
|
||||
watcher->connect(
|
||||
watcher.get(), &decltype(watcher)::element_type::finished, &app, h);
|
||||
// also monitor progress
|
||||
auto progress = [fut](int) {
|
||||
auto max = fut.progressMaximum();
|
||||
auto min = fut.progressMinimum();
|
||||
auto current = fut.progressValue();
|
||||
std::cout << "copy progress " << current << " (" << min << " -> " << max
|
||||
<< ")" << std::endl;
|
||||
};
|
||||
watcher->connect(watcher.get(),
|
||||
&decltype(watcher)::element_type::progressValueChanged, &app, progress);
|
||||
return {watcher, nullptr};
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
if (argc < 2)
|
||||
return -1;
|
||||
|
||||
Retainer r;
|
||||
if (argc == 2) {
|
||||
r = do_query(app, argv[1]);
|
||||
} else if (argc == 3) {
|
||||
r = do_copy(app, argv[1], argv[2]);
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
98
cmake/external/glib/cppgir/examples/gio.cpp
vendored
Normal file
98
cmake/external/glib/cppgir/examples/gio.cpp
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
#define GI_INLINE 1
|
||||
#include <gio/gio.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace GLib = gi::repository::GLib;
|
||||
namespace Gio = gi::repository::Gio;
|
||||
|
||||
const std::string localhost("127.0.0.1");
|
||||
static GLib::MainLoop loop;
|
||||
|
||||
// many calls here support GError
|
||||
// so typically will throw here instead
|
||||
// (unless GError output is explicitly requested in call signature)
|
||||
|
||||
// NOTE abundancy of gi::expect not generally needed;
|
||||
// only needed when using --dl and --expected
|
||||
|
||||
static bool
|
||||
receive(Gio::Socket s, GLib::IOCondition /*cond*/)
|
||||
{
|
||||
guint8 buffer[1024] = {
|
||||
0,
|
||||
};
|
||||
Gio::SocketAddress a;
|
||||
int count = gi::expect(s.receive_from(&a, buffer, sizeof(buffer)));
|
||||
if (count > 0) {
|
||||
// let's see where it came from
|
||||
std::string origin("someone");
|
||||
auto ia = gi::object_cast<Gio::InetSocketAddress>(a);
|
||||
if (ia) {
|
||||
origin = gi::expect(gi::expect(ia.get_address()).to_string());
|
||||
origin += ":";
|
||||
origin += std::to_string(gi::expect(ia.get_port()));
|
||||
}
|
||||
std::cout << origin << " said " << (char *)buffer << std::endl;
|
||||
// quit when idle
|
||||
GLib::idle_add([]() {
|
||||
loop.quit();
|
||||
return GLib::SOURCE_REMOVE_;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Gio::Socket
|
||||
open(bool listen)
|
||||
{
|
||||
auto socket = gi::expect(Gio::Socket::new_(Gio::SocketFamily::IPV4_,
|
||||
Gio::SocketType::DATAGRAM_, Gio::SocketProtocol::DEFAULT_));
|
||||
|
||||
auto address =
|
||||
gi::expect(Gio::InetSocketAddress::new_from_string(localhost, 0));
|
||||
socket.bind(address, false);
|
||||
socket.set_blocking(false);
|
||||
|
||||
if (listen) {
|
||||
// runtime introspection has a hard time here,
|
||||
// but with a bit of extra information, we can keep going
|
||||
#if defined(GI_CALL_ARGS) && CALL_ARGS <= 1
|
||||
// so we should have this signature for a function with 1 non-required
|
||||
GLib::Source source =
|
||||
gi::expect(socket.create_source({.condition = GLib::IOCondition::IN_}));
|
||||
#else
|
||||
GLib::Source source =
|
||||
gi::expect(socket.create_source(GLib::IOCondition::IN_, nullptr));
|
||||
#endif
|
||||
source.set_callback<Gio::SocketSourceFunc>(receive);
|
||||
source.attach();
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
static void
|
||||
die(const std::string &why)
|
||||
{
|
||||
std::cerr << why << std::endl;
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
die("missing argument");
|
||||
|
||||
std::string msg = argv[1];
|
||||
std::cout << "will send message " << msg << std::endl;
|
||||
|
||||
auto recv = open(true);
|
||||
auto local = gi::expect(recv.get_local_address());
|
||||
auto send = open(false);
|
||||
send.send_to(local, (guint8 *)msg.data(), msg.size(), nullptr);
|
||||
|
||||
loop = gi::expect(GLib::MainLoop::new_());
|
||||
loop.run();
|
||||
}
|
||||
178
cmake/external/glib/cppgir/examples/gobject.cpp
vendored
Normal file
178
cmake/external/glib/cppgir/examples/gobject.cpp
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
// no code generation is needed
|
||||
// all required functionality is required by the basic code
|
||||
#include <gi/gi.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace GObject_ = gi::repository::GObject;
|
||||
|
||||
static const int DEFAULT_AGE = 10;
|
||||
static const char *PERSON_TYPE = "GIPerson";
|
||||
|
||||
// class must be a ObjectImpl to support properties and signals
|
||||
class Person : public GObject_::impl::ObjectImpl
|
||||
{
|
||||
using self_type = Person;
|
||||
|
||||
private:
|
||||
// the implementation/definition part of the properties
|
||||
// the setup parameters shadow the corresponding g_param_spec_xxx
|
||||
// so in practice define the property (name, nick, description)
|
||||
// along with min, max, default and so (where applicable)
|
||||
// (could also be in a constructor initializer list,
|
||||
// but this way it applies to any constructor)
|
||||
// this provides interface to set/get the actual value
|
||||
gi::property<int> prop_age_{this, "age", "age", "age", 0, 100, DEFAULT_AGE};
|
||||
gi::property<std::string> prop_firstname_{
|
||||
this, "firstname", "firstname", "firstname", ""};
|
||||
gi::property<std::string> prop_lastname_{
|
||||
this, "lastname", "lastname", "lastname", ""};
|
||||
|
||||
public:
|
||||
// likewise for signal
|
||||
// public because there is no extra interface for owning class
|
||||
// (both owner and outside can connect and/or emit)
|
||||
// btw, using Person in this signature would not be the way to go,
|
||||
// should stick to a plain wrapped type
|
||||
gi::signal<void(GObject_::Object, int)> signal_trigger{this, "trigger"};
|
||||
// std::string also works here,
|
||||
// but that would cost an extra allocation during invocation
|
||||
gi::signal<void(GObject_::Object, gi::cstring_v)> signal_example{
|
||||
this, "example"};
|
||||
|
||||
public:
|
||||
// old-style C++ first
|
||||
Person() : ObjectImpl(this) {}
|
||||
|
||||
// new-style C-first;
|
||||
// must use proper signature for constructor and superclass
|
||||
// this also registers a "public" GType, whose name must be specified
|
||||
Person(const InitData &id) : ObjectImpl(this, id, PERSON_TYPE) {}
|
||||
|
||||
// usually the above constructor is also used to during GType registration,
|
||||
// as such "dummy instantiation" auto-magically collects property info, etc)
|
||||
// however, it does involve an "incomplete" instantiation (with no .gobj_())
|
||||
// if such is considered too inelegant or too costly, then alternatively
|
||||
// a ::get_type_() can be defined that is then used to register type instead
|
||||
// however, as the example below demonstrates, this may be much less ergonomic
|
||||
// so it is likely only useful if really so desired or in advanced cases)
|
||||
// (e.g. subclass-ing a subclass)
|
||||
static GType get_type_disabled() // not really used due to extra suffix
|
||||
{
|
||||
// if this is used, the member initializers only need to mention name
|
||||
// no longer the full spec(ification)
|
||||
// (as the name then only serves to link the propertyspec to actual storage)
|
||||
return register_type_<Person>(PERSON_TYPE, 0, {},
|
||||
{{&self_type::prop_age_, "age", "age", "age", 0, 100, DEFAULT_AGE},
|
||||
{&self_type::prop_firstname_, "firstname", "firstname", "firstname",
|
||||
""},
|
||||
{&self_type::prop_lastname_, "lastname", "lastname", "lastname",
|
||||
""}},
|
||||
{{&self_type::signal_trigger, "trigger"},
|
||||
{&self_type::signal_example, "example"}});
|
||||
}
|
||||
|
||||
// the public counterpart providing the same interface
|
||||
// as with any wrapped object's predefined properties
|
||||
gi::property_proxy<int> prop_age() { return prop_age_.get_proxy(); }
|
||||
|
||||
gi::property_proxy<std::string> prop_firstname()
|
||||
{
|
||||
return prop_firstname_.get_proxy();
|
||||
}
|
||||
|
||||
gi::property_proxy<std::string> prop_lastname()
|
||||
{
|
||||
return prop_lastname_.get_proxy();
|
||||
}
|
||||
|
||||
void action(int id)
|
||||
{
|
||||
std::cout << "Changing the properties of 'p'" << std::endl;
|
||||
prop_firstname_ = "John";
|
||||
prop_lastname_ = "Doe";
|
||||
prop_age_ = 43;
|
||||
std::cout << "Done changing the properties of 'p'" << std::endl;
|
||||
// we were triggered after all
|
||||
signal_trigger.emit(id);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
on_firstname_changed(GObject_::Object, GObject_::ParamSpec)
|
||||
{
|
||||
std::cout << "- firstname changed!" << std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
on_lastname_changed(GObject_::Object, GObject_::ParamSpec)
|
||||
{
|
||||
std::cout << "- lastname changed!" << std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
on_age_changed(GObject_::Object, GObject_::ParamSpec)
|
||||
{
|
||||
std::cout << "- age changed!" << std::endl;
|
||||
}
|
||||
|
||||
int
|
||||
main(int /*argc*/, char ** /*argv*/)
|
||||
{
|
||||
Person p;
|
||||
// should have default age
|
||||
assert(p.prop_age().get() == DEFAULT_AGE);
|
||||
// register some handlers that will be called when the values of the
|
||||
// specified parameters are changed
|
||||
p.prop_firstname().signal_notify().connect(&on_firstname_changed);
|
||||
p.prop_lastname().signal_notify().connect(on_lastname_changed);
|
||||
p.prop_age().signal_notify().connect(&on_age_changed);
|
||||
|
||||
// now change the properties and see that the handlers get called
|
||||
p.action(0);
|
||||
|
||||
// (derived) object can be constructed on stack for simple cases
|
||||
// but in other (real) cases it is recommended that it is heap based.
|
||||
// so as not to have a naked ptr, it can be managed by a (special) shared
|
||||
// ptr that uses the GObject refcount for (shared) ownership tracking
|
||||
auto dp = gi::make_ref<Person, gi::construct_cpp_t>();
|
||||
// however, the GObject world has no knowledge of the subclass
|
||||
// (each instance is 1-to-1 with Person instance though).
|
||||
// so when we get something from that world, we can (sort-of dynamic)
|
||||
// cast to the subclass
|
||||
auto l = [](GObject_::Object ob, int id) {
|
||||
std::cout << " - triggered id " << id << std::endl;
|
||||
// obtain Person
|
||||
auto lp = gi::ref_ptr_cast<Person>(ob);
|
||||
if (lp)
|
||||
std::cout << " - it was a person!" << std::endl;
|
||||
// it really should be ...
|
||||
assert(lp);
|
||||
};
|
||||
dp->signal_trigger.connect(l);
|
||||
dp->action(1);
|
||||
|
||||
// all of the above constructed Person in C++ centric style
|
||||
// that is, as part of the constructor (super class) execution,
|
||||
// a (derived) GObject is created and associated/extended with C++ side Person
|
||||
|
||||
// alternatively, another derived GObject type can be registered,
|
||||
// which will trigger construction of a Person (as part of g_object_new()),
|
||||
// and then again associate those
|
||||
// the following essentially runs g_object_new()
|
||||
static int CUSTOM_AGE = 5;
|
||||
auto dpc = gi::make_ref<Person, gi::construct_c_t>("age", CUSTOM_AGE);
|
||||
// this will have a different GType
|
||||
assert(dpc->gobj_type_() != dp->gobj_type_());
|
||||
// as registered using specified name
|
||||
assert(g_type_from_name(PERSON_TYPE) == dpc->gobj_type_());
|
||||
// and the right age as specified in (gobject style) construct params
|
||||
assert(dpc->prop_age().get() == CUSTOM_AGE);
|
||||
|
||||
// if the class supports C-style, it is selected automatically
|
||||
auto dpa = gi::make_ref<Person>();
|
||||
// should have ended up with C-style GType
|
||||
assert(dpa->gobj_type_() == dpc->gobj_type_());
|
||||
|
||||
return 0;
|
||||
}
|
||||
419
cmake/external/glib/cppgir/examples/gst.cpp
vendored
Normal file
419
cmake/external/glib/cppgir/examples/gst.cpp
vendored
Normal file
@@ -0,0 +1,419 @@
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "assert.h"
|
||||
|
||||
#ifndef USE_GI_MODULE
|
||||
// no inline as the implementation is provided by other TUs
|
||||
// #define GI_INLINE 1
|
||||
#include <gst/gst.hpp>
|
||||
#else
|
||||
// optional, but recommended if gi macros are used
|
||||
#include <gi/gi_inc.hpp>
|
||||
// we also use some gst macros and api
|
||||
#include <gst/gst.h>
|
||||
// import used modules
|
||||
import gi.repo.gobject;
|
||||
import gi.repo.gst;
|
||||
#endif
|
||||
|
||||
namespace GLib = gi::repository::GLib;
|
||||
namespace Gst = gi::repository::Gst;
|
||||
namespace GObject_ = gi::repository::GObject;
|
||||
|
||||
void
|
||||
say(const std::string &msg)
|
||||
{
|
||||
std::cout << msg << std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
die(const std::string &why)
|
||||
{
|
||||
std::cerr << why << std::endl;
|
||||
exit(2);
|
||||
}
|
||||
|
||||
class Player
|
||||
{
|
||||
typedef Player self;
|
||||
|
||||
GLib::MainLoop loop_;
|
||||
std::string url_;
|
||||
|
||||
Gst::Element playbin_;
|
||||
bool live_ = false;
|
||||
Gst::Element vfilter_;
|
||||
Gst::Element afilter_;
|
||||
std::map<std::string, Gst::Element> filter_;
|
||||
GLib::SourceScopedConnection monitor_;
|
||||
GObject_::SignalConnection busconn_;
|
||||
|
||||
public:
|
||||
Player(GLib::MainLoop loop, const std::string &url) : loop_(loop), url_(url)
|
||||
{
|
||||
playbin_ = Gst::ElementFactory::make("playbin");
|
||||
assert(playbin_);
|
||||
// get a property
|
||||
// this one is known at introspection time
|
||||
// (so type is known and suitable checked)
|
||||
auto name = playbin_.property_name().get();
|
||||
std::cout << "created playbin " << name << std::endl;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
// set some property
|
||||
playbin_.set_property("uri", url_);
|
||||
// ... or several ones
|
||||
playbin_.set_properties("volume", 0.5, "mute", false);
|
||||
// ... non-primitive also possible
|
||||
vfilter_ = Gst::ElementFactory::make("identity", "vfilter");
|
||||
afilter_ = Gst::ElementFactory::make("identity", "afilter");
|
||||
playbin_.set_properties("video-filter", vfilter_, "audio-filter", afilter_);
|
||||
|
||||
// connect some; find out what the source element is
|
||||
// signal not known to introspection, so have to provide signature here
|
||||
playbin_.connect<void(Gst::Element, Gst::Element)>(
|
||||
"source-setup", gi::mem_fun(&self::on_source_setup, this));
|
||||
// for a known signal, it is a bit easier to connect
|
||||
auto bus = playbin_.get_bus();
|
||||
bus.add_signal_watch();
|
||||
// signal connect; using introspected (hence checked) signal definition
|
||||
// could do without the slot step here and directly pass the lambda,
|
||||
// but wrapping it this way allows combining this with the returned
|
||||
// (plain) id into a scoped connection guard
|
||||
auto slot = bus.signal_message().slot(gi::mem_fun(&self::on_message, this));
|
||||
busconn_ =
|
||||
gi::make_connection(bus.signal_message().connect(slot), slot, bus);
|
||||
// again, if the guard is not desired; the following simply suffices
|
||||
if (false)
|
||||
bus.signal_message().connect(gi::mem_fun(&self::on_message, this));
|
||||
// if the signal signature is somehow not supported
|
||||
// then the following is a fallback manual method
|
||||
// which still allows to use lambda (and such)
|
||||
// and also provides some ownership management
|
||||
if (false) {
|
||||
auto h = [](GstBus *, GstMessage *) {
|
||||
// dummy no-op
|
||||
};
|
||||
bus.connect_unchecked<void(GstBus *, GstMessage *)>("message", h);
|
||||
}
|
||||
// likewise, if a callback is to be used whose arguments are not supported
|
||||
// or in a function that is not supported, then the following is a fallback
|
||||
// which still allows to use lambda (and such)
|
||||
// and also provides some ownership management
|
||||
if (false) {
|
||||
auto h = []() { return false; };
|
||||
auto cb = new gi::callback_wrapper<gboolean(), false>(h);
|
||||
// now the above pointer is managed as user data
|
||||
// and will be suitable destroyed when needed
|
||||
g_idle_add_full(0, &cb->wrapper, cb, &cb->destroy);
|
||||
// in case of a typical single-use async callback (with no GDestroyNotify)
|
||||
// use true as AUTODESTROY template parameter
|
||||
// (then it will auto-clean up after invoking callback)
|
||||
}
|
||||
// alternatively, maybe there is some API that accepts a Closure
|
||||
// the following are some convenient ways to get a closure
|
||||
if (false) {
|
||||
GLib::LogFunc h = [](gi::cstring_v log_domain,
|
||||
GLib::LogLevelFlags log_level,
|
||||
gi::cstring_v message) {
|
||||
(void)log_domain;
|
||||
(void)log_level;
|
||||
(void)message;
|
||||
};
|
||||
GObject_::Closure::from_callback(h);
|
||||
}
|
||||
// likewise, but with a bit more manual (C++) signature specification
|
||||
if (false) {
|
||||
auto h = [](GObject_::Object, int) {};
|
||||
GObject_::Closure::from_functor<void(GObject_::Object, int)>(h);
|
||||
}
|
||||
|
||||
say("Setting pipeline to PAUSED ...");
|
||||
auto ret = playbin_.set_state(Gst::State::PAUSED_);
|
||||
if (ret == Gst::StateChangeReturn::FAILURE_) {
|
||||
die("Pipeline does not want to pause");
|
||||
} else if (ret == Gst::StateChangeReturn::NO_PREROLL_) {
|
||||
say("Pipeline is live and does not need PREROLL ...");
|
||||
live_ = true;
|
||||
} else if (ret == Gst::StateChangeReturn::ASYNC_) {
|
||||
say("Pipeline is PREROLLING ...");
|
||||
}
|
||||
|
||||
// inspect after a while
|
||||
GLib::timeout_add_seconds(2, [this]() {
|
||||
inspect();
|
||||
return GLib::SOURCE_REMOVE_;
|
||||
});
|
||||
|
||||
// some regular progress reporting ...
|
||||
GLib::SourceFunc func = [this]() {
|
||||
progress();
|
||||
return GLib::SOURCE_CONTINUE_;
|
||||
};
|
||||
// the introspected function returns a plain id,
|
||||
// which can be used in the usual way to disconnect
|
||||
// e.g. at destructor time of owning object
|
||||
// alternatively, a helper scoped object can take care of that
|
||||
monitor_ = gi::make_connection(GLib::timeout_add_seconds(1, func), func);
|
||||
// other such make_connection variations exist;
|
||||
// e.g. for a signal connection, a probe callback
|
||||
// (and can easily be custom added)
|
||||
|
||||
// add a pad probe; use a casual lambda
|
||||
// but not too casual, mind (dangling) references/pointers though
|
||||
filter_["video"] = vfilter_;
|
||||
filter_["audio"] = afilter_;
|
||||
for (auto &&p : filter_) {
|
||||
if (p.second) {
|
||||
Gst::Pad pad = p.second.get_static_pad("sink");
|
||||
auto name = p.first;
|
||||
auto handler = [name](Gst::Pad p, Gst::PadProbeInfo_Ref info) {
|
||||
auto s = p.get_path_string();
|
||||
s += "received " + name + " buffer";
|
||||
auto buffer = info.get_buffer();
|
||||
if (buffer) {
|
||||
s += " of size ";
|
||||
s += std::to_string(buffer.get_size());
|
||||
}
|
||||
return Gst::PadProbeReturn::REMOVE_;
|
||||
};
|
||||
pad.add_probe(Gst::PadProbeType::BUFFER_, handler);
|
||||
}
|
||||
}
|
||||
|
||||
// shamelessly demo some helpers that aid in caps/value handling
|
||||
auto caps = Gst::Caps::new_empty_simple("video/x-raw");
|
||||
caps.set_value("width", GObject_::Value(Gst::IntRange(240, 320)));
|
||||
caps.set_value("pixel-aspect-ratio", GObject_::Value(Gst::Fraction(4, 3)));
|
||||
caps.set_value(
|
||||
"framerate", GObject_::Value(Gst::FractionRange({25, 1}, 30)));
|
||||
// retrieving pretty much the same way
|
||||
auto s = caps.get_structure(0);
|
||||
auto par = s.get_value("pixel-aspect-ratio").get_value<Gst::Fraction>();
|
||||
// helpers also stream to string properly
|
||||
std::ostringstream oss;
|
||||
oss << par;
|
||||
oss << (Gst::FlagSet(1, 1) == Gst::FlagSet(2, 1));
|
||||
// silly test code to exercise some operators
|
||||
// Rank override allows for succinct numeric conversion
|
||||
if (+Gst::Rank::PRIMARY_ + 0) {
|
||||
// flags support various typical operations
|
||||
(void)(Gst::PadProbeType::BUFFER_ | Gst::PadProbeType::BUFFER_LIST_);
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if (playbin_)
|
||||
playbin_.set_state(Gst::State::NULL_);
|
||||
loop_.quit();
|
||||
}
|
||||
|
||||
void on_message(Gst::Bus /*bus*/, Gst::Message_Ref msg)
|
||||
{
|
||||
auto &&src = msg.src_();
|
||||
switch (msg.type_()) {
|
||||
case Gst::MessageType::EOS_:
|
||||
say("Got EOS from " + src.get_path_string());
|
||||
stop();
|
||||
break;
|
||||
case Gst::MessageType::ERROR_: {
|
||||
GLib::Error err;
|
||||
gi::cstring debug;
|
||||
msg.parse_error(&err, &debug);
|
||||
say("Got error from " + src.get_path_string());
|
||||
if (debug.size())
|
||||
say("debug info:\n" + debug);
|
||||
break;
|
||||
}
|
||||
case Gst::MessageType::STATE_CHANGED_:
|
||||
/* only handle top-level case */
|
||||
if (src != playbin_)
|
||||
break;
|
||||
Gst::State old, new_, pending;
|
||||
msg.parse_state_changed(&old, &new_, &pending);
|
||||
if (new_ == Gst::State::PAUSED_ && old == Gst::State::READY_)
|
||||
playbin_.set_state(Gst::State::PLAYING_);
|
||||
break;
|
||||
default:
|
||||
// never mind
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void on_source_setup(Gst::Element /*pb*/, Gst::Element src)
|
||||
{
|
||||
say("source is " + src.get_path_string());
|
||||
}
|
||||
|
||||
void inspect()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
// this should work
|
||||
auto bin = gi::object_cast<Gst::Bin>(playbin_);
|
||||
assert(bin);
|
||||
// a dynamically loaded element may implement a number of interfaces
|
||||
// which is not known at compile-time
|
||||
// the cast above can also cast to interface, which can be obtained as
|
||||
// follows if known at introspection/compile time
|
||||
auto cp = bin.interface_(gi::interface_tag<Gst::ChildProxy>());
|
||||
oss << "player bin has " << cp.get_children_count() << " children"
|
||||
<< std::endl;
|
||||
// get some properties (dynamically, i.e. not known at introspection
|
||||
// time) type will have to match (i.e. transformable) at runtime
|
||||
auto n_v = playbin_.get_property<int>("n-video");
|
||||
auto n_a = playbin_.get_property<int>("n-audio");
|
||||
// minimal stuff
|
||||
oss << "sample streams: video=" << n_v << ", audio=" << n_a << std::endl;
|
||||
// show some tag info
|
||||
for (auto &&p :
|
||||
std::map<std::string, int>{{"video", n_v}, {"audio", n_a}}) {
|
||||
for (int i = 0; i < p.second; ++i) {
|
||||
// the argument's type should match the signal definition
|
||||
// (cast if needed to make it so)
|
||||
auto action = std::string("get-");
|
||||
action += p.first + "-tags";
|
||||
auto taglist = playbin_.emit<Gst::TagList>(action, i);
|
||||
if (!taglist)
|
||||
continue;
|
||||
auto ntags = taglist.n_tags();
|
||||
oss << p.first << " stream " << i << std::endl;
|
||||
for (int j = 0; j < ntags; ++j) {
|
||||
auto tname = taglist.nth_tag_name(j);
|
||||
auto value = taglist.get_value_index(tname, 0);
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
auto sval = value.transform_value<std::string>();
|
||||
oss << " " << tname << ": " << sval << std::endl;
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
} catch (...) {
|
||||
// could be object or otherwise, never mind
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// should also have caps here by now
|
||||
// there are other ways to obtain this, but let's go this way here
|
||||
for (auto &&p : filter_) {
|
||||
if (p.second) {
|
||||
Gst::Pad pad = p.second.get_static_pad("sink");
|
||||
auto caps = pad.get_current_caps();
|
||||
oss << p.first << " caps: " << caps.to_string() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
say(oss.str());
|
||||
|
||||
say("Playbin elements:");
|
||||
// Gst::Iterator could be used with native interface
|
||||
// but a helper wrapper has been provided that supports ease-of-use as
|
||||
// in ...
|
||||
for (auto &&el : Gst::IteratorAdapter<Gst::Element>(bin.iterate_recurse()))
|
||||
say(el.get_path_string());
|
||||
say("");
|
||||
}
|
||||
|
||||
static std::string time_to_str(Gst::ClockTime time)
|
||||
{
|
||||
// could be done otherwise
|
||||
// but we have native C access at hand, so let's use that
|
||||
auto s = g_strdup_printf("%" GST_TIME_FORMAT, GST_TIME_ARGS(time));
|
||||
std::string ret(s);
|
||||
g_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void progress()
|
||||
{
|
||||
bool ok = true;
|
||||
gint64 duration = -1, position = -1;
|
||||
ok &= playbin_.query_duration(Gst::Format::TIME_, &duration);
|
||||
ok &= playbin_.query_position(Gst::Format::TIME_, &position);
|
||||
std::ostringstream oss;
|
||||
if (ok) {
|
||||
oss << "Duration: " << time_to_str(duration)
|
||||
<< ", Position: " << time_to_str(position);
|
||||
} else {
|
||||
oss << "No progress info available";
|
||||
}
|
||||
say(oss.str());
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef GI_CLASS_IMPL
|
||||
// not used in the above, but serves as a subclass example
|
||||
class ChattyBin : public Gst::impl::BinImpl
|
||||
{
|
||||
public:
|
||||
#if 0
|
||||
// this part is only needed if there is some conflict
|
||||
// (among members of class and/or interfaces)
|
||||
// otherwise it should be auto-detected
|
||||
struct DefinitionData
|
||||
{
|
||||
GI_DEFINES_MEMBER(BinClassDef, add_element, true)
|
||||
};
|
||||
#endif
|
||||
|
||||
ChattyBin() : Gst::impl::BinImpl(this) {}
|
||||
|
||||
bool add_element_(Gst::Element element) noexcept override
|
||||
{
|
||||
say("adding element " + element.name_() + '\n');
|
||||
return Gst::impl::BinImpl::add_element_(element);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
die("missing argument");
|
||||
|
||||
// C signature fits C main best anyway
|
||||
gst_init(&argc, &argv);
|
||||
|
||||
std::string url = argv[1];
|
||||
// make it URL if not so
|
||||
if (!Gst::Uri::is_valid(url)) {
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
url = gi::expect(Gst::filename_to_uri(url));
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
} catch (const GLib::Error &ex) {
|
||||
die(ex.what());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
say("Playing " + url);
|
||||
|
||||
// simply local var will do here
|
||||
auto loop = GLib::MainLoop::new_();
|
||||
Player player(loop, url);
|
||||
|
||||
// schedule start
|
||||
GLib::idle_add([&] {
|
||||
player.start();
|
||||
return GLib::SOURCE_REMOVE_;
|
||||
});
|
||||
// ... and auto end after a while
|
||||
GLib::timeout_add_seconds(10, [&] {
|
||||
player.stop();
|
||||
return GLib::SOURCE_REMOVE_;
|
||||
});
|
||||
|
||||
loop.run();
|
||||
}
|
||||
95
cmake/external/glib/cppgir/examples/gtk-builder.ui
vendored
Normal file
95
cmake/external/glib/cppgir/examples/gtk-builder.ui
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkListStore" id="liststore1">
|
||||
<columns>
|
||||
<column type="gchararray"/>
|
||||
<column type="gchararray"/>
|
||||
<column type="gint"/>
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">John</col>
|
||||
<col id="1" translatable="yes">Doe</col>
|
||||
<col id="2">25</col>
|
||||
<col id="3" translatable="yes">This is the John Doe row</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Mary</col>
|
||||
<col id="1" translatable="yes">Unknown</col>
|
||||
<col id="2">50</col>
|
||||
<col id="3" translatable="yes">This is the Mary Unknown row</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="default-height">250</property>
|
||||
<property name="default-width">440</property>
|
||||
<property name="title" translatable="yes">Gtk::Builder demo</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox1">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="toolbar1">
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="label" translatable="yes">Help</property>
|
||||
<property name="tooltip-text" translatable="yes">Help</property>
|
||||
<property name="action-name">win.help</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="treeview1">
|
||||
<property name="model">liststore1</property>
|
||||
<property name="tooltip-column">3</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="column1">
|
||||
<property name="title">Name</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer1"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="column2">
|
||||
<property name="title">Surname</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer2"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="column3">
|
||||
<property name="title">Age</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="renderer3"/>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<!-- INSERT -->
|
||||
<child>
|
||||
<object class="GtkStatusbar" id="statusbar1"/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
2
cmake/external/glib/cppgir/examples/gtk-obj.cpp
vendored
Normal file
2
cmake/external/glib/cppgir/examples/gtk-obj.cpp
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#define GI_INCLUDE_IMPL 1
|
||||
#include <gtk/gtk.hpp>
|
||||
429
cmake/external/glib/cppgir/examples/gtk.cpp
vendored
Normal file
429
cmake/external/glib/cppgir/examples/gtk.cpp
vendored
Normal file
@@ -0,0 +1,429 @@
|
||||
#ifndef USE_GI_MODULE
|
||||
// no inline as the implementation is in another TU
|
||||
// #define GI_INLINE 1
|
||||
#include <gtk/gtk.hpp>
|
||||
#else
|
||||
// optional, but recommended if gi macros are used
|
||||
#include <gi/gi_inc.hpp>
|
||||
// we use some gtk macros
|
||||
#include <gtk/gtk.h>
|
||||
// import recursive module
|
||||
import gi.repo.gtk.rec;
|
||||
#endif
|
||||
|
||||
// adapt to API as needed
|
||||
#if GTK_CHECK_VERSION(4, 0, 0)
|
||||
#define GTK4 1
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace GLib = gi::repository::GLib;
|
||||
namespace GObject_ = gi::repository::GObject;
|
||||
namespace Gtk = gi::repository::Gtk;
|
||||
|
||||
static GLib::MainLoop loop;
|
||||
|
||||
#ifdef GI_CLASS_IMPL
|
||||
// based on python-gtk3 example
|
||||
// https://python-gtk-3-tutorial.readthedocs.io/en/latest/treeview.html
|
||||
|
||||
// list of tuples for each software,
|
||||
// containing the software name, initial release, and main programming languages
|
||||
const std::vector<std::tuple<std::string, int, std::string>> software_list{
|
||||
std::make_tuple("Firefox", 2002, "C++"),
|
||||
std::make_tuple("Eclipse", 2004, "Java"),
|
||||
std::make_tuple("Pitivi", 2004, "Python"),
|
||||
std::make_tuple("Netbeans", 1996, "Java"),
|
||||
std::make_tuple("Chrome", 2008, "C++"),
|
||||
std::make_tuple("Filezilla", 2001, "C++"),
|
||||
std::make_tuple("Bazaar", 2005, "Python"),
|
||||
std::make_tuple("Git", 2005, "C"),
|
||||
std::make_tuple("Linux Kernel", 1991, "C"),
|
||||
std::make_tuple("GCC", 1987, "C"),
|
||||
std::make_tuple("Frostwire", 2004, "Java")};
|
||||
|
||||
class TreeViewFilterWindow : public Gtk::impl::WindowImpl
|
||||
{
|
||||
typedef TreeViewFilterWindow self_type;
|
||||
|
||||
Gtk::ListStore store_;
|
||||
Gtk::TreeModelFilter language_filter_;
|
||||
std::string current_filter_language_;
|
||||
|
||||
public:
|
||||
TreeViewFilterWindow() : Gtk::impl::WindowImpl(this)
|
||||
{
|
||||
Gtk::Window &self = *(this);
|
||||
self.set_title("TreeView filter demo");
|
||||
#ifdef GTK4
|
||||
#else
|
||||
self.set_border_width(10);
|
||||
#endif
|
||||
|
||||
// set up the grid in which elements are positioned
|
||||
auto grid = Gtk::Grid::new_();
|
||||
grid.set_column_homogeneous(true);
|
||||
grid.set_row_homogeneous(true);
|
||||
#ifdef GTK4
|
||||
self.set_child(grid);
|
||||
#if 0
|
||||
// this could work, but the annotation for .load_from_data
|
||||
// is too unstable across versions
|
||||
{ // migrate call above to CSS style
|
||||
const char *css = "grid { margin: 10px; }";
|
||||
auto provider = Gtk::CssProvider::new_();
|
||||
provider.load_from_data((guint8 *)css, -1);
|
||||
grid.get_style_context().add_provider(
|
||||
provider, Gtk::STYLE_PROVIDER_PRIORITY_USER_);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
self.add(grid);
|
||||
#endif
|
||||
|
||||
// create ListStore model
|
||||
store_ = Gtk::ListStore::new_type_<std::string, int, std::string>();
|
||||
for (auto &e : software_list) {
|
||||
auto it = store_.append();
|
||||
GObject_::Value cols[] = {std::get<0>(e), std::get<1>(e), std::get<2>(e)};
|
||||
for (unsigned i = 0; i < G_N_ELEMENTS(cols); ++i) {
|
||||
store_.set_value(it, i, cols[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// create the filter, feeding it with the liststore model
|
||||
auto treemodel = store_.interface_(gi::interface_tag<Gtk::TreeModel>());
|
||||
language_filter_ =
|
||||
gi::object_cast<Gtk::TreeModelFilter>(treemodel.filter_new(nullptr));
|
||||
// set the filter function
|
||||
language_filter_.set_visible_func(
|
||||
gi::mem_fun(&self_type::language_filter_func, this));
|
||||
|
||||
// create the treeview, make it use the filter as a model, and add
|
||||
// columns
|
||||
auto treeview = Gtk::TreeView::new_with_model(language_filter_);
|
||||
int i = 0;
|
||||
for (auto &e : {"Software", "Release Year", "Programming Language"}) {
|
||||
auto renderer = Gtk::CellRendererText::new_();
|
||||
auto column = Gtk::TreeViewColumn::new_(e, renderer, {{"text", i}});
|
||||
treeview.append_column(column);
|
||||
++i;
|
||||
}
|
||||
|
||||
// create buttons to filter by programming language, and set up their
|
||||
// events
|
||||
std::vector<Gtk::Widget> buttons;
|
||||
for (auto &prog_language : {"Java", "C", "C++", "Python", "None"}) {
|
||||
auto button = Gtk::Button::new_with_label(prog_language);
|
||||
buttons.push_back(button);
|
||||
button.signal_clicked().connect(
|
||||
gi::mem_fun(&self_type::on_selection_button_clicked, this));
|
||||
}
|
||||
|
||||
// set up the layout;
|
||||
// put the treeview in a scrollwindow, and the buttons in a row
|
||||
auto scrollable_treelist = Gtk::ScrolledWindow::new_();
|
||||
scrollable_treelist.set_vexpand(true);
|
||||
grid.attach(scrollable_treelist, 0, 0, 8, 10);
|
||||
grid.attach_next_to(
|
||||
buttons[0], scrollable_treelist, Gtk::PositionType::BOTTOM_, 1, 1);
|
||||
auto it = buttons.begin() + 1;
|
||||
while (it != buttons.end()) {
|
||||
grid.attach_next_to(*it, *(it - 1), Gtk::PositionType::RIGHT_, 1, 1);
|
||||
++it;
|
||||
}
|
||||
#ifdef GTK4
|
||||
scrollable_treelist.set_child(treeview);
|
||||
self.show();
|
||||
#else
|
||||
scrollable_treelist.add(treeview);
|
||||
self.show_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool language_filter_func(Gtk::TreeModel filter, Gtk::TreeIter_Ref it) const
|
||||
{
|
||||
if (current_filter_language_.empty() || current_filter_language_ == "None")
|
||||
return true;
|
||||
return current_filter_language_ ==
|
||||
filter.get_value(it, 2).get_value<std::string>();
|
||||
}
|
||||
|
||||
void on_selection_button_clicked(Gtk::Button button)
|
||||
{
|
||||
// set the current language filter to the button's label
|
||||
current_filter_language_ = button.get_label();
|
||||
std::cout << current_filter_language_ << " language selected!" << std::endl;
|
||||
// update the filter, which updates in turn the view
|
||||
language_filter_.refilter();
|
||||
}
|
||||
};
|
||||
|
||||
// other part based on gtkmm builder example
|
||||
namespace Gio = gi::repository::Gio;
|
||||
|
||||
auto templ = R"|(
|
||||
<interface>
|
||||
<template class="FooWidget" parent="GtkBox">
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<property name="spacing">4</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="hello_button">
|
||||
<property name="label">Hello World</property>
|
||||
<signal name="clicked" handler="hello_button_clicked"/>
|
||||
<signal name="clicked" handler="hello_button_clicked_object" object="label" swapped="no"/>
|
||||
<signal name="clicked" handler="hello_button_clicked_object_swapped" object="label" swapped="yes"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="goodbye_button">
|
||||
<property name="label">Goodbye World</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label">
|
||||
<property name="label">Greetings World</property>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
)|";
|
||||
|
||||
auto templ_child = R"|(
|
||||
<child>
|
||||
<object class="FooWidget" id="foowidget1"/>
|
||||
</child>
|
||||
)|";
|
||||
|
||||
using WidgetTemplateHelper = Gtk::impl::WidgetTemplateHelper;
|
||||
|
||||
class FooWidget : public Gtk::impl::BoxImpl, public WidgetTemplateHelper
|
||||
{
|
||||
using self_type = FooWidget;
|
||||
|
||||
public:
|
||||
static void custom_class_init(GtkBoxClass *klass, gpointer)
|
||||
{
|
||||
// this is similar to the C case
|
||||
// plain C functions are used, as klass struct has no direct equivalent
|
||||
auto wklass = GTK_WIDGET_CLASS(klass);
|
||||
auto bytes = GLib::Bytes::new_static((const guint8 *)templ, strlen(templ));
|
||||
gtk_widget_class_set_template(wklass, bytes.gobj_());
|
||||
gtk_widget_class_bind_template_child_full(wklass, "hello_button", TRUE, 0);
|
||||
// chain up
|
||||
WidgetTemplateHelper::custom_class_init<gi::register_type<self_type>>(
|
||||
klass, nullptr);
|
||||
}
|
||||
|
||||
public:
|
||||
Gtk::Button hello_;
|
||||
|
||||
void on_hello(Gtk::Button) { std::cout << "Hi" << std::endl; }
|
||||
|
||||
void on_hello_tail(Gtk::Button b, Gtk::Label l)
|
||||
{
|
||||
g_assert(l.gobj_type_() == GTK_TYPE_LABEL);
|
||||
g_assert(b.gobj_type_() == GTK_TYPE_BUTTON);
|
||||
std::cout << "Hi tail" << std::endl;
|
||||
}
|
||||
|
||||
void on_hello_head(Gtk::Label l, Gtk::Button b)
|
||||
{
|
||||
g_assert(l.gobj_type_() == GTK_TYPE_LABEL);
|
||||
g_assert(b.gobj_type_() == GTK_TYPE_BUTTON);
|
||||
std::cout << "Hi head" << std::endl;
|
||||
}
|
||||
|
||||
FooWidget(const InitData &id)
|
||||
: Gtk::impl::BoxImpl(this, id, "FooWidget"),
|
||||
WidgetTemplateHelper(object_())
|
||||
{
|
||||
// skip registration case
|
||||
if (!id) {
|
||||
// as we have defined a get_type_() below, that should not happen
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
}
|
||||
|
||||
// locate object
|
||||
hello_ = gi::object_cast<Gtk::Button>(
|
||||
get_template_child(gobj_type_(), "hello_button"));
|
||||
|
||||
// setup signal
|
||||
// NOTE the "manual" signature here should suitably match
|
||||
// (also considering object/swapped flags, etc)
|
||||
auto ok = set_handler<void(Gtk::Button)>(
|
||||
"hello_button_clicked", gi::mem_fun(&self_type::on_hello, this));
|
||||
g_assert(ok);
|
||||
ok = set_handler<void(Gtk::Button, Gtk::Label), ConnectObject::TAIL>(
|
||||
"hello_button_clicked_object",
|
||||
gi::mem_fun(&self_type::on_hello_tail, this));
|
||||
g_assert(ok);
|
||||
ok = set_handler<void(Gtk::Label, Gtk::Button), ConnectObject::HEAD>(
|
||||
"hello_button_clicked_object_swapped",
|
||||
gi::mem_fun(&self_type::on_hello_head, this));
|
||||
g_assert(ok);
|
||||
}
|
||||
|
||||
static GType get_type_()
|
||||
{
|
||||
return register_type_<FooWidget>("FooWidget", 0, {}, {}, {});
|
||||
}
|
||||
};
|
||||
|
||||
class ExampleWindow : public Gtk::impl::WindowImpl
|
||||
{
|
||||
using self_type = ExampleWindow;
|
||||
|
||||
public:
|
||||
ExampleWindow(Gtk::Window base, Gtk::Builder builder)
|
||||
: Gtk::impl::WindowImpl(base, this, "ExampleWindow")
|
||||
// custom name parameter is optional, but can be provided if used in .ui
|
||||
{
|
||||
(void)builder;
|
||||
setup();
|
||||
}
|
||||
|
||||
// name parameter is required and specifies registered type as-is
|
||||
// (so some proper namespace prefix is advisable in real case)
|
||||
ExampleWindow(const InitData &id)
|
||||
: Gtk::impl::WindowImpl(this, id, "ExampleWindow")
|
||||
{
|
||||
// skip registration case
|
||||
if (id)
|
||||
setup();
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
auto actions = Gio::SimpleActionGroup::new_();
|
||||
auto am = Gio::ActionMap(actions);
|
||||
auto action = Gio::SimpleAction::new_("help");
|
||||
action.signal_activate().connect(gi::mem_fun(&self_type::on_help, this));
|
||||
am.add_action(action);
|
||||
insert_action_group("win", actions);
|
||||
}
|
||||
|
||||
void on_help(Gio::Action, GLib::Variant) { std::cout << "Help" << std::endl; }
|
||||
|
||||
// subclass_type == 0; UI specifies GtkWindow
|
||||
// subclass_type < 0; UI specifies GIOBJECT__ExampleWindow
|
||||
// in either cases above; manual C++ association is needed
|
||||
// (uses first constructor)
|
||||
// -> NOT recommended
|
||||
// subclass_type > 0; UI specifies ExampleWindow;
|
||||
// all is properly created by (C-side) instance construction
|
||||
// (uses second constructor)
|
||||
// -> recommended
|
||||
// subclass_type > 1; also insert a FooWidget
|
||||
// (also all created by C-side construction)
|
||||
static Gtk::Window build(int subclass_type)
|
||||
{
|
||||
const char *UIFILE = G_STRINGIFY(EXAMPLES_DIR) "/gtk-builder.ui";
|
||||
const char *WINID = "window1";
|
||||
|
||||
auto builder = Gtk::Builder::new_();
|
||||
// if the subtype has additional functionality (e.g. method overrides)
|
||||
// then builder should instantiate using that type,
|
||||
// otherwise base type suffices
|
||||
// NOTE in the former case, there is a "short time" that the GObject side
|
||||
// exists without a corresponding C++ side, so any attempt to use the
|
||||
// extended parts (e.g. method calls) will be unfortunate
|
||||
if (subclass_type) {
|
||||
std::string WINCLASS = "GtkWindow";
|
||||
std::ifstream f(UIFILE, std::ios::binary);
|
||||
std::string ui;
|
||||
std::getline(f, ui, '\0');
|
||||
|
||||
auto index = ui.find(WINCLASS);
|
||||
assert(index != ui.npos);
|
||||
ui.replace(ui.begin() + index, ui.begin() + index + WINCLASS.size(),
|
||||
subclass_type < 0 ? "GIOBJECT__ExampleWindow" : "ExampleWindow");
|
||||
if (subclass_type > 1) {
|
||||
// sprinkle a template instance
|
||||
std::string MARKER = "<!-- INSERT -->";
|
||||
index = ui.find(MARKER);
|
||||
ui.replace(ui.begin() + index, ui.begin() + index + MARKER.size(),
|
||||
templ_child);
|
||||
// ensure type registered
|
||||
gi::register_type<FooWidget>();
|
||||
}
|
||||
// now we got a UI file that references the subclass type
|
||||
// ensure the latter is registered
|
||||
if (subclass_type < 0) {
|
||||
// use a temporary instance
|
||||
gi::make_ref<ExampleWindow, gi::construct_cpp_t>(nullptr, builder);
|
||||
} else {
|
||||
// a new cleaner way
|
||||
gi::register_type<ExampleWindow>();
|
||||
}
|
||||
builder.add_from_string(ui, -1);
|
||||
} else {
|
||||
builder.add_from_file(UIFILE);
|
||||
}
|
||||
if (false) {
|
||||
// some compile checks
|
||||
builder.get_object(WINID);
|
||||
builder.get_object<Gtk::Window>(WINID);
|
||||
}
|
||||
if (subclass_type > 1) {
|
||||
// verify proper C++ side setup
|
||||
auto foo_obj =
|
||||
builder.get_object<FooWidget::baseclass_type>("foowidget1");
|
||||
assert(foo_obj);
|
||||
auto foo = gi::ref_ptr_cast<FooWidget>(foo_obj);
|
||||
assert(foo);
|
||||
assert(foo->hello_);
|
||||
}
|
||||
// the special _derived may be needed to setup C++ side and associate
|
||||
return subclass_type > 0 ? builder.get_object<Gtk::Window>(WINID)
|
||||
: builder.get_object_derived<self_type>(WINID);
|
||||
}
|
||||
};
|
||||
#endif // GI_CLASS_IMPL
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
#ifdef GTK4
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
gtk_init();
|
||||
#else
|
||||
gtk_init(&argc, &argv);
|
||||
#endif
|
||||
|
||||
loop = GLib::MainLoop::new_();
|
||||
|
||||
// recommended general approach iso stack based
|
||||
// too much vmethod calling which is not safe for plain case
|
||||
Gtk::Window win;
|
||||
#ifdef GI_CLASS_IMPL
|
||||
// silly compile/link check
|
||||
static_assert(gi::transfer_full.value == 1, "");
|
||||
if (argc == gi::transfer_full.value) {
|
||||
win = gi::make_ref<TreeViewFilterWindow>();
|
||||
} else {
|
||||
win = ExampleWindow::build(std::stoi(argv[1]));
|
||||
}
|
||||
// TODO auto-handle arg ignore ??
|
||||
#ifdef GTK4
|
||||
win.signal_close_request().connect([](Gtk::Window) {
|
||||
loop.quit();
|
||||
return true;
|
||||
});
|
||||
win.show();
|
||||
#else
|
||||
win.signal_destroy().connect([](Gtk::Widget) { loop.quit(); });
|
||||
win.show_all();
|
||||
#endif
|
||||
#else // GI_CLASS_IMPL
|
||||
(void)win;
|
||||
#endif
|
||||
|
||||
loop.run();
|
||||
}
|
||||
31
cmake/external/glib/cppgir/expected-lite/.editorconfig
vendored
Normal file
31
cmake/external/glib/cppgir/expected-lite/.editorconfig
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# Configuration file for EditorConfig, see https://EditorConfig.org
|
||||
|
||||
# Ignore any other files further up in the file system
|
||||
root = true
|
||||
|
||||
# All files:
|
||||
[*]
|
||||
# Let git determine line ending: end_of_line = lf
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Markdown files: keep trailing space-pair as line-break
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# Python scripts:
|
||||
[*.py]
|
||||
|
||||
# YAML scripts:
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
# Makefiles: Tab indentation (no size specified)
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
# C, C++ source files:
|
||||
[*.{h,hpp,c,cpp}]
|
||||
26
cmake/external/glib/cppgir/expected-lite/.gitattributes
vendored
Normal file
26
cmake/external/glib/cppgir/expected-lite/.gitattributes
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
# Custom for CodeBlocks
|
||||
*.cbp text eol=lf
|
||||
*.workspace text eol=lf
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
*.sln merge=union
|
||||
*.csproj merge=union
|
||||
*.vbproj merge=union
|
||||
*.fsproj merge=union
|
||||
*.dbproj merge=union
|
||||
|
||||
# Standard to msysgit
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
97
cmake/external/glib/cppgir/expected-lite/.github/workflows/ci.yml
vendored
Normal file
97
cmake/external/glib/cppgir/expected-lite/.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
name: CI
|
||||
|
||||
env:
|
||||
PROJECT: EXPECTED_LITE
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
gcc:
|
||||
strategy:
|
||||
matrix:
|
||||
version: [8, 9, 10, 11]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install GCC ${{ matrix.version }}
|
||||
run: sudo apt-get install -y gcc-${{ matrix.version }} g++-${{ matrix.version }}
|
||||
|
||||
- name: Configure tests
|
||||
env:
|
||||
CXX: g++-${{ matrix.version }}
|
||||
run: cmake -S . -B build
|
||||
-D CMAKE_BUILD_TYPE:STRING=Release
|
||||
-D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON
|
||||
-D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON
|
||||
-D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF
|
||||
|
||||
- name: Build tests
|
||||
run: cmake --build build -j 4
|
||||
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
run: ctest --output-on-failure -j 4
|
||||
|
||||
clang:
|
||||
strategy:
|
||||
matrix:
|
||||
version: [8, 9, 10, 11, 12]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Clang ${{ matrix.version }}
|
||||
run: sudo apt-get install -y clang-${{ matrix.version }}
|
||||
|
||||
- name: Configure tests
|
||||
env:
|
||||
CXX: clang-${{ matrix.version }}
|
||||
run: cmake -S . -B build
|
||||
-D CMAKE_CXX_COMPILER=clang++
|
||||
-D CMAKE_BUILD_TYPE:STRING=Release
|
||||
-D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON
|
||||
-D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON
|
||||
-D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF
|
||||
|
||||
- name: Build tests
|
||||
run: cmake --build build -j 4
|
||||
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
run: ctest --output-on-failure -j 4
|
||||
|
||||
msvc:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-2019, windows-2022]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Configure tests
|
||||
run: cmake -S . -B build
|
||||
-D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON
|
||||
-D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON
|
||||
-D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF
|
||||
|
||||
- name: Build tests
|
||||
run: cmake --build build --config Release -j 4
|
||||
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
run: ctest -C Release --output-on-failure -j 4
|
||||
|
||||
41
cmake/external/glib/cppgir/expected-lite/.gitignore
vendored
Normal file
41
cmake/external/glib/cppgir/expected-lite/.gitignore
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
test/expected.t
|
||||
|
||||
# Build folder
|
||||
/build/
|
||||
|
||||
# CodeBlocks IDE files
|
||||
*.layout
|
||||
|
||||
# Visual Studio Code
|
||||
/.vscode/
|
||||
|
||||
# Visual Studio
|
||||
/.vs/
|
||||
4
cmake/external/glib/cppgir/expected-lite/.tgitconfig
vendored
Normal file
4
cmake/external/glib/cppgir/expected-lite/.tgitconfig
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[bugtraq]
|
||||
url = https://github.com/martinmoene/expected-lite/issues/%BUGID%
|
||||
number = true
|
||||
logregex = "(\\s*(,|and)?\\s*#\\d+)+\n(\\d+)"
|
||||
166
cmake/external/glib/cppgir/expected-lite/.travis.yml
vendored
Normal file
166
cmake/external/glib/cppgir/expected-lite/.travis.yml
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
os: linux
|
||||
dist: trusty
|
||||
sudo: false
|
||||
group: travis_latest
|
||||
language: c++
|
||||
cache: ccache
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources: &apt_sources
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.5
|
||||
- llvm-toolchain-precise-3.6
|
||||
- llvm-toolchain-precise-3.7
|
||||
- llvm-toolchain-precise-3.8
|
||||
- llvm-toolchain-trusty-3.9
|
||||
- llvm-toolchain-trusty-4.0
|
||||
- llvm-toolchain-trusty-5.0
|
||||
- llvm-toolchain-trusty-6.0
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
env: COMPILER=g++-4.8
|
||||
compiler: gcc
|
||||
addons: &gcc4_8
|
||||
apt:
|
||||
packages: ["g++-4.8", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=g++-4.9
|
||||
compiler: gcc
|
||||
addons: &gcc4_9
|
||||
apt:
|
||||
packages: ["g++-4.9", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=g++-5
|
||||
compiler: gcc
|
||||
addons: &gcc5
|
||||
apt:
|
||||
packages: ["g++-5", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=g++-6
|
||||
compiler: gcc
|
||||
addons: &gcc6
|
||||
apt:
|
||||
packages: ["g++-6", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=g++-7
|
||||
compiler: gcc
|
||||
addons: &gcc7
|
||||
apt:
|
||||
packages: ["g++-7", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=g++-8
|
||||
compiler: gcc
|
||||
addons: &gcc8
|
||||
apt:
|
||||
packages: ["g++-8", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=clang++-3.5
|
||||
compiler: clang
|
||||
addons: &clang3_5
|
||||
apt:
|
||||
packages: ["clang-3.5", "g++-7", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=clang++-3.6
|
||||
compiler: clang
|
||||
addons: &clang3_6
|
||||
apt:
|
||||
packages: ["clang-3.6", "g++-7", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=clang++-3.7
|
||||
compiler: clang
|
||||
addons: &clang3-7
|
||||
apt:
|
||||
packages: ["clang-3.7", "g++-7", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=clang++-3.8
|
||||
compiler: clang
|
||||
addons: &clang3_8
|
||||
apt:
|
||||
packages: ["clang-3.8", "g++-7", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=clang++-3.9
|
||||
compiler: clang
|
||||
addons: &clang3_9
|
||||
apt:
|
||||
packages: ["clang-3.9", "g++-7", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=clang++-4.0
|
||||
compiler: clang
|
||||
addons: &clang4_0
|
||||
apt:
|
||||
packages: ["clang-4.0", "g++-7", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=clang++-5.0
|
||||
compiler: clang
|
||||
addons: &clang5_0
|
||||
apt:
|
||||
packages: ["clang-5.0", "g++-7", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: linux
|
||||
env: COMPILER=clang++-6.0
|
||||
compiler: clang
|
||||
addons: &clang6_0
|
||||
apt:
|
||||
packages: ["clang-6.0", "g++-7", "python3-pip", "lcov"]
|
||||
sources: *apt_sources
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode7.3
|
||||
compiler: clang
|
||||
env: COMPILER='clang++'
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode8
|
||||
compiler: clang
|
||||
env: COMPILER='clang++'
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode9
|
||||
compiler: clang
|
||||
env: COMPILER='clang++'
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode10
|
||||
compiler: clang
|
||||
env: COMPILER='clang++'
|
||||
|
||||
allow_failures:
|
||||
- env: COMPILER=clang++-3.5
|
||||
|
||||
fast_finish: true
|
||||
|
||||
script:
|
||||
- export CXX=${COMPILER}
|
||||
- JOBS=2 # Travis machines have 2 cores.
|
||||
- mkdir build && cd build
|
||||
- cmake -G "Unix Makefiles" -DEXPECTED_LITE_OPT_SELECT_NONSTD=ON -DEXPECTED_LITE_OPT_BUILD_TESTS=ON -DEXPECTED_LITE_OPT_BUILD_EXAMPLES=OFF ..
|
||||
- cmake --build . -- -j${JOBS}
|
||||
- ctest --output-on-failure -j${JOBS}
|
||||
5
cmake/external/glib/cppgir/expected-lite/CHANGES.txt
vendored
Normal file
5
cmake/external/glib/cppgir/expected-lite/CHANGES.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
Changes for expected lite
|
||||
|
||||
version 0.0 2016-03-13
|
||||
|
||||
- Initial code commit.
|
||||
130
cmake/external/glib/cppgir/expected-lite/CMakeLists.txt
vendored
Normal file
130
cmake/external/glib/cppgir/expected-lite/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
# Copyright 2016-2018 by Martin Moene
|
||||
#
|
||||
# https://github.com/martinmoene/expected-lite
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
cmake_minimum_required( VERSION 3.5 FATAL_ERROR )
|
||||
|
||||
# expected-lite project and version, updated by script/update-version.py:
|
||||
|
||||
project(
|
||||
expected_lite
|
||||
VERSION 0.6.2
|
||||
# DESCRIPTION "Expected objects in C++11 and later in a single-file header-only library"
|
||||
# HOMEPAGE_URL "https://github.com/martinmoene/expected-lite"
|
||||
LANGUAGES CXX )
|
||||
|
||||
# Package information:
|
||||
|
||||
set( unit_name "expected" )
|
||||
set( package_nspace "nonstd" )
|
||||
set( package_name "${unit_name}-lite" )
|
||||
set( package_version "${${PROJECT_NAME}_VERSION}" )
|
||||
|
||||
message( STATUS "Project '${PROJECT_NAME}', package '${package_name}' version: '${package_version}'")
|
||||
|
||||
# Toplevel or subproject:
|
||||
|
||||
if ( CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME )
|
||||
set( expected_IS_TOPLEVEL_PROJECT TRUE )
|
||||
else()
|
||||
set( expected_IS_TOPLEVEL_PROJECT FALSE )
|
||||
endif()
|
||||
|
||||
# If toplevel project, enable building and performing of tests, disable building of examples:
|
||||
|
||||
option( EXPECTED_LITE_OPT_BUILD_TESTS "Build and perform expected-lite tests" ${expected_IS_TOPLEVEL_PROJECT} )
|
||||
option( EXPECTED_LITE_OPT_BUILD_EXAMPLES "Build expected-lite examples" OFF )
|
||||
set( EXPEXTED_P0323R "99" STRING "Specify proposal revision compatibility (99: latest)" )
|
||||
|
||||
option( EXPECTED_LITE_OPT_SELECT_STD "Select std::expected" OFF )
|
||||
option( EXPECTED_LITE_OPT_SELECT_NONSTD "Select nonstd::expected" OFF )
|
||||
|
||||
# If requested, build and perform tests, build examples:
|
||||
|
||||
if ( EXPECTED_LITE_OPT_BUILD_TESTS )
|
||||
enable_testing()
|
||||
add_subdirectory( test )
|
||||
endif()
|
||||
|
||||
if ( EXPECTED_LITE_OPT_BUILD_EXAMPLES )
|
||||
add_subdirectory( example )
|
||||
endif()
|
||||
|
||||
#
|
||||
# Interface, installation and packaging
|
||||
#
|
||||
|
||||
# CMake helpers:
|
||||
|
||||
include( GNUInstallDirs )
|
||||
include( CMakePackageConfigHelpers )
|
||||
|
||||
# Interface library:
|
||||
|
||||
add_library(
|
||||
${package_name} INTERFACE )
|
||||
|
||||
add_library(
|
||||
${package_nspace}::${package_name} ALIAS ${package_name} )
|
||||
|
||||
target_include_directories(
|
||||
${package_name}
|
||||
INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" )
|
||||
|
||||
# Package configuration:
|
||||
# Note: package_name and package_target are used in package_config_in
|
||||
|
||||
set( package_folder "${package_name}" )
|
||||
set( package_target "${package_name}-targets" )
|
||||
set( package_config "${package_name}-config.cmake" )
|
||||
set( package_config_in "${package_name}-config.cmake.in" )
|
||||
set( package_config_version "${package_name}-config-version.cmake" )
|
||||
|
||||
configure_package_config_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_in}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${package_config}"
|
||||
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}"
|
||||
)
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_version}.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" @ONLY
|
||||
)
|
||||
|
||||
# Installation:
|
||||
|
||||
install(
|
||||
TARGETS ${package_name}
|
||||
EXPORT ${package_target}
|
||||
# INCLUDES DESTINATION "${...}" # already set via target_include_directories()
|
||||
)
|
||||
|
||||
install(
|
||||
EXPORT ${package_target}
|
||||
NAMESPACE ${package_nspace}::
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}"
|
||||
)
|
||||
|
||||
install(
|
||||
FILES "${CMAKE_CURRENT_BINARY_DIR}/${package_config}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}"
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY "include/"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
)
|
||||
|
||||
export(
|
||||
EXPORT ${package_target}
|
||||
NAMESPACE ${package_nspace}::
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-targets.cmake"
|
||||
)
|
||||
|
||||
# end of file
|
||||
23
cmake/external/glib/cppgir/expected-lite/LICENSE.txt
vendored
Normal file
23
cmake/external/glib/cppgir/expected-lite/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
9
cmake/external/glib/cppgir/expected-lite/Notes.md
vendored
Normal file
9
cmake/external/glib/cppgir/expected-lite/Notes.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Plan
|
||||
----
|
||||
|
||||
- [ ] Implement expected.then() etc.
|
||||
- [x] Write CMake files
|
||||
- [x] Check code against current Dxxxxr0 and adapt
|
||||
- [x] Expand README.md
|
||||
- [x] Expand test/expected.t.cpp
|
||||
- [x] Improve use of travis matrix
|
||||
459
cmake/external/glib/cppgir/expected-lite/README.md
vendored
Normal file
459
cmake/external/glib/cppgir/expected-lite/README.md
vendored
Normal file
@@ -0,0 +1,459 @@
|
||||
# expected lite: expected objects for C++11 and later
|
||||
|
||||
[](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [](https://opensource.org/licenses/BSL-1.0) [](https://github.com/martinmoene/expected-lite/actions/workflows/ci.yml) [](https://travis-ci.org/martinmoene/expected-lite) [](https://ci.appveyor.com/project/martinmoene/expected-lite) [](https://github.com/martinmoene/expected-lite/releases) [](https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp) [](https://conan.io/center/expected-lite) [](https://wandbox.org/permlink/MnnwqOtE8ZQ4rRsv) [](https://godbolt.org/z/9BuMZx)
|
||||
|
||||
*expected lite* is a single-file header-only library for objects that either represent a valid value or an error that you can pass by value. It is intended for use with C++11 and later. The library is based on the [std::expected](http://wg21.link/p0323) proposal [1] .
|
||||
|
||||
**Contents**
|
||||
- [Example usage](#example-usage)
|
||||
- [In a nutshell](#in-a-nutshell)
|
||||
- [License](#license)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Installation](#installation)
|
||||
- [Synopsis](#synopsis)
|
||||
- [Comparison with like types](#comparison)
|
||||
- [Reported to work with](#reported-to-work-with)
|
||||
- [Implementation notes](#implementation-notes)
|
||||
- [Other implementations of expected](#other-implementations-of-expected)
|
||||
- [Notes and references](#notes-and-references)
|
||||
- [Appendix](#appendix)
|
||||
|
||||
## Example usage
|
||||
|
||||
```Cpp
|
||||
#include "nonstd/expected.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace nonstd;
|
||||
using namespace std::literals;
|
||||
|
||||
auto to_int( char const * const text ) -> expected<int, std::string>
|
||||
{
|
||||
char * pos = nullptr;
|
||||
auto value = strtol( text, &pos, 0 );
|
||||
|
||||
if ( pos != text ) return value;
|
||||
else return make_unexpected( "'"s + text + "' isn't a number" );
|
||||
}
|
||||
|
||||
int main( int argc, char * argv[] )
|
||||
{
|
||||
auto text = argc > 1 ? argv[1] : "42";
|
||||
|
||||
auto ei = to_int( text );
|
||||
|
||||
if ( ei ) std::cout << "'" << text << "' is " << *ei << ", ";
|
||||
else std::cout << "Error: " << ei.error();
|
||||
}
|
||||
```
|
||||
|
||||
### Compile and run
|
||||
|
||||
```
|
||||
prompt> g++ -std=c++14 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe 123 && 01-basic.exe abc
|
||||
'123' is 123, Error: 'abc' isn't a number
|
||||
```
|
||||
|
||||
## In a nutshell
|
||||
|
||||
**expected lite** is a single-file header-only library to represent value objects that either contain a valid value or an error. The library is a partly implementation of the proposal for [std::expected](http://wg21.link/p0323) [1,2,3] for use with C++11 and later.
|
||||
|
||||
**Some Features and properties of expected lite** are ease of installation (single header), default and explicit construction of an expected, construction and assignment from a value that is convertible to the underlying type, copy- and move-construction and copy- and move-assignment from another expected of the same type, testing for the presence of a value, operators for unchecked access to the value or the error (pointer or reference), value() and value_or() for checked access to the value, relational operators, swap() and various factory functions.
|
||||
|
||||
*expected lite* shares the approach to in-place tags with [any-lite](https://github.com/martinmoene/any-lite), [optional-lite](https://github.com/martinmoene/optional-lite) and with [variant-lite](https://github.com/martinmoene/variant-lite) and these libraries can be used together.
|
||||
|
||||
**Not provided** are reference-type expecteds. *expected lite* doesn't honour triviality of value and error types. *expected lite* doesn't handle overloaded *address of* operators.
|
||||
|
||||
For more examples, see [1].
|
||||
|
||||
## License
|
||||
|
||||
*expected lite* is distributed under the [Boost Software License](https://github.com/martinmoene/XXXX-lite/blob/master/LICENSE.txt).
|
||||
|
||||
## Dependencies
|
||||
|
||||
*expected lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header).
|
||||
|
||||
## Installation
|
||||
|
||||
*expected lite* is a single-file header-only library. Put `expected.hpp` directly into the project source tree or somewhere reachable from your project.
|
||||
|
||||
## Synopsis
|
||||
|
||||
**Contents**
|
||||
- [Configuration](#configuration)
|
||||
- [Types in namespace nonstd](#types-in-namespace-nonstd)
|
||||
- [Interface of expected](#interface-of-expected)
|
||||
- [Algorithms for expected](#algorithms-for-expected)
|
||||
- [Interface of unexpected_type](#interface-of-unexpected_type)
|
||||
- [Algorithms for unexpected_type](#algorithms-for-unexpected_type)
|
||||
|
||||
### Configuration
|
||||
|
||||
#### Tweak header
|
||||
|
||||
If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *expected lite* supports the [tweak header](https://vector-of-bool.github.io/2020/10/04/lib-configuration.html) mechanism. Provide your *tweak header* as `nonstd/expected.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define expected_CPLUSPLUS 201103L`.
|
||||
|
||||
#### Standard selection macro
|
||||
|
||||
\-D<b>nsel\_CPLUSPLUS</b>=199711L
|
||||
Define this macro to override the auto-detection of the supported C++ standard, or if your compiler does not set the `__cplusplus` macro correctly.
|
||||
|
||||
#### Select `std::expected` or `nonstd::expected`
|
||||
|
||||
At default, *expected lite* uses `std::expected` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::expected` or expected lite's `nonstd::expected` as `nonstd::expected` via the following macros.
|
||||
|
||||
-D<b>nsel\_CONFIG\_SELECT\_EXPECTED</b>=nsel_EXPECTED_DEFAULT
|
||||
Define this to `nsel_EXPECTED_STD` to select `std::expected` as `nonstd::expected`. Define this to `nsel_EXPECTED_NONSTD` to select `nonstd::expected` as `nonstd::expected`. Default is undefined, which has the same effect as defining to `nsel_EXPECTED_DEFAULT`.
|
||||
|
||||
-D<b>nsel\_P0323R</b>=7 *(default)*
|
||||
Define this to the proposal revision number to control the presence and behavior of features (see tables). Default is 7 for the latest revision.
|
||||
|
||||
#### Disable C++ exceptions
|
||||
|
||||
-D<b>nsel\_CONFIG\_NO\_EXCEPTIONS</b>=0
|
||||
Define this to 1 if you want to compile without exceptions. If not defined, the header tries and detect if exceptions have been disabled (e.g. via `-fno-exceptions` or `/kernel`). Default determined in header.
|
||||
|
||||
#### Enable SEH exceptions
|
||||
|
||||
-D<b>nsel\_CONFIG\_NO\_EXCEPTIONS\_SEH</b>=0
|
||||
Define this to 1 or 0 to control the use of SEH when C++ exceptions are disabled (see above). If not defined, the header tries and detect if SEH is available if C++ exceptions have been disabled (e.g. via `-fno-exceptions` or `/kernel`). Default determined in header.
|
||||
|
||||
#### Enable compilation errors
|
||||
|
||||
\-D<b>nsel\_CONFIG\_CONFIRMS\_COMPILATION\_ERRORS</b>=0
|
||||
Define this macro to 1 to experience the by-design compile-time errors of the library in the test suite. Default is 0.
|
||||
|
||||
### Types in namespace nonstd
|
||||
|
||||
| Purpose | Type | Note / Object |
|
||||
|-----------------|------|---------------|
|
||||
| Expected | template<typename T, typename E = std::exception_ptr><br>class **expected**; | nsel_P0323 <= 2 |
|
||||
| Expected | template<typename T, typename E><br>class **expected**; | nsel_P0323 > 2 |
|
||||
| Error type | template<typename E><br>class **unexpected_type**; | |
|
||||
| Error type | template<><br>class **unexpected_type**<std::exception_ptr>; | nsel_P0323 <= 2 |
|
||||
| Error type | template<typename E><br>class **unexpected**; | >= C++17 |
|
||||
| Traits | template<typename E><br>struct **is_unexpected**; | nsel_P0323 <= 3 |
|
||||
| In-place value construction | struct **in_place_t**; | in_place_t in_place{}; |
|
||||
| In-place error construction | struct **in_place_unexpected_t**; | in_place_unexpected_t<br>unexpect{}; |
|
||||
| In-place error construction | struct **in_place_unexpected_t**; | in_place_unexpected_t<br>in_place_unexpected{}; |
|
||||
| Error reporting | class **bad_expected_access**; | |
|
||||
|
||||
### Interface of expected
|
||||
|
||||
| Kind | Method | Result |
|
||||
|--------------|-------------------------------------------------------------------------|--------|
|
||||
| Construction | [constexpr] **expected**() noexcept(...) | an object with default value |
|
||||
| | [constexpr] **expected**( expected const & other ) | initialize to contents of other |
|
||||
| | [constexpr] **expected**( expected && other ) | move contents from other |
|
||||
| | [constexpr] **expected**( value_type const & value ) | initialize to value |
|
||||
| | [constexpr] **expected**( value_type && value ) noexcept(...) | move from value |
|
||||
| | [constexpr] explicit **expected**( in_place_t, Args&&... args ) | construct value in-place from args |
|
||||
| | [constexpr] explicit **expected**( in_place_t,<br> std::initializer_list<U> il, Args&&... args ) | construct value in-place from args |
|
||||
| | [constexpr] **expected**( unexpected_type<E> const & error ) | initialize to error |
|
||||
| | [constexpr] **expected**( unexpected_type<E> && error ) | move from error |
|
||||
| | [constexpr] explicit **expected**( in_place_unexpected_t,<br> Args&&... args ) | construct error in-place from args |
|
||||
| | [constexpr] explicit **expected**( in_place_unexpected_t,<br> std::initializer_list<U> il, Args&&... args )| construct error in-place from args |
|
||||
| Destruction | ~**expected**() | destruct current content |
|
||||
| Assignment | expected **operator=**( expected const & other ) | assign contents of other;<br>destruct current content, if any |
|
||||
| | expected & **operator=**( expected && other ) noexcept(...) | move contents of other |
|
||||
| | expected & **operator=**( U && v ) | move value from v |
|
||||
| | expected & **operator=**( unexpected_type<E> const & u ) | initialize to unexpected |
|
||||
| | expected & **operator=**( unexpected_type<E> && u ) | move from unexpected |
|
||||
| | template<typename... Args><br>void **emplace**( Args &&... args ) | emplace from args |
|
||||
| | template<typename U, typename... Args><br>void **emplace**( std::initializer_list<U> il, Args &&... args ) | emplace from args |
|
||||
| Swap | void **swap**( expected & other ) noexcept | swap with other |
|
||||
| Observers | constexpr value_type const \* **operator->**() const | pointer to current content (const);<br>must contain value |
|
||||
| | value_type \* **operator->**() | pointer to current content (non-const);<br>must contain value |
|
||||
| | constexpr value_type const & **operator \***() const & | the current content (const ref);<br>must contain value |
|
||||
| | constexpr value_type && **operator \***() && | the current content (non-const ref);<br>must contain value |
|
||||
| | constexpr explicit operator **bool**() const noexcept | true if contains value |
|
||||
| | constexpr **has_value**() const noexcept | true if contains value |
|
||||
| | constexpr value_type const & **value**() const & | current content (const ref);<br>see [note 1](#note1) |
|
||||
| | value_type & **value**() & | current content (non-const ref);<br>see [note 1](#note1) |
|
||||
| | constexpr value_type && **value**() && | move from current content;<br>see [note 1](#note1) |
|
||||
| | constexpr error_type const & **error**() const & | current error (const ref);<br>must contain error |
|
||||
| | error_type & **error**() & | current error (non-const ref);<br>must contain error |
|
||||
| | constexpr error_type && **error**() && | move from current error;<br>must contain error |
|
||||
| | constexpr unexpected_type<E> **get_unexpected**() const | the error as unexpected<>;<br>must contain error |
|
||||
| | template<typename Ex><br>bool **has_exception**() const | true of contains exception (as base) |
|
||||
| | value_type **value_or**( U && v ) const & | value or move from v |
|
||||
| | value_type **value_or**( U && v ) && | move from value or move from v |
|
||||
| | ... | |
|
||||
|
||||
<a id="note1"></a>Note 1: checked access: if no content, for std::exception_ptr rethrows error(), otherwise throws bad_expected_access(error()).
|
||||
|
||||
### Algorithms for expected
|
||||
|
||||
| Kind | Function |
|
||||
|---------------------------------|----------|
|
||||
| Comparison with expected | |
|
||||
| == != | template<typename T1, typename E1, typename T2, typename E2><br>constexpr bool operator ***op***(<br> expected<T1,E1> const & x,<br> expected<T2,E2> const & y ) |
|
||||
| Comparison with expected | nsel_P0323R <= 2 |
|
||||
| < > <= >= | template<typename T, typename E><br>constexpr bool operator ***op***(<br> expected<T,E> const & x,<br> expected<T,E> const & y ) |
|
||||
| Comparison with unexpected_type | |
|
||||
| == != | template<typename T1, typename E1, typename E2><br>constexpr bool operator ***op***(<br> expected<T1,E1> const & x,<br> unexpected_type<E2> const & u ) |
|
||||
| | template<typename T1, typename E1, typename E2><br>constexpr bool operator ***op***(<br> unexpected_type<E2> const & u,<br> expected<T1,E1> const & x ) |
|
||||
| Comparison with unexpected_type | nsel_P0323R <= 2 |
|
||||
| < > <= >= | template<typename T, typename E><br>constexpr bool operator ***op***(<br> expected<T,E> const & x,<br> unexpected_type<E> const & u ) |
|
||||
| | template<typename T, typename E><br>constexpr bool operator ***op***(<br> unexpected_type<E> const & u,<br> expected<T,E> const & x ) |
|
||||
| Comparison with T | |
|
||||
| == != | template<typename T, typename E><br>constexpr bool operator ***op***(<br> expected<T,E> const & x,<br> T const & v ) |
|
||||
| | template<typename T, typename E><br>constexpr bool operator ***op***(<br> T const & v,<br> expected<T,E> const & x ) |
|
||||
| Comparison with T | nsel_P0323R <= 2 |
|
||||
| < > <= >= | template<typename T, typename E><br>constexpr bool operator ***op***(<br> expected<T,E> const & x,<br> T const & v ) |
|
||||
| | template<typename T, typename E><br>constexpr bool operator ***op***(<br> T const & v,<br> expected<T,E> const & x ) |
|
||||
| Specialized algorithms | |
|
||||
| Swap | template<typename T, typename E><br>void **swap**(<br> expected<T,E> & x,<br> expected<T,E> & y ) noexcept( noexcept( x.swap(y) ) ) |
|
||||
| Make expected from | nsel_P0323R <= 3 |
|
||||
|  Value | template<typename T><br>constexpr auto **make_expected**( T && v ) -><br> expected< typename std::decay<T>::type> |
|
||||
|  Nothing | auto **make_expected**() -> expected<void> |
|
||||
|  Current exception | template<typename T><br>constexpr auto **make_expected_from_current_exception**() -> expected<T> |
|
||||
|  Exception | template<typename T><br>auto **make_expected_from_exception**( std::exception_ptr v ) -> expected<T>|
|
||||
|  Error | template<typename T, typename E><br>constexpr auto **make_expected_from_error**( E e ) -><br> expected<T, typename std::decay<E>::type> |
|
||||
|  Call | template<typename F><br>auto **make_expected_from_call**( F f ) -><br> expected< typename std::result_of<F()>::type>|
|
||||
|  Call, void specialization | template<typename F><br>auto **make_expected_from_call**( F f ) -> expected<void> |
|
||||
|
||||
### Interface of unexpected_type
|
||||
|
||||
| Kind | Method | Result |
|
||||
|--------------|-----------------------------------------------------------|--------|
|
||||
| Construction | **unexpected_type**() = delete; | no default construction |
|
||||
| | constexpr explicit **unexpected_type**( E const & error ) | copy-constructed from an E |
|
||||
| | constexpr explicit **unexpected_type**( E && error ) | move-constructed from an E |
|
||||
| Observers | constexpr error_type const & **value**() const | can observe contained error |
|
||||
| | error_type & **value**() | can modify contained error |
|
||||
|
||||
### Algorithms for unexpected_type
|
||||
|
||||
| Kind | Function |
|
||||
|-------------------------------|----------|
|
||||
| Comparison with unexpected | |
|
||||
| == != | template<typename E><br>constexpr bool operator ***op***(<br> unexpected_type<E> const & x,<br> unexpected_type<E> const & y ) |
|
||||
| Comparison with unexpected | nsel_P0323R <= 2 |
|
||||
| < > <= >= | template<typename E><br>constexpr bool operator ***op***(<br> unexpected_type<E> const & x,<br> unexpected_type<E> const & y ) |
|
||||
| Comparison with exception_ptr | |
|
||||
| == != | constexpr bool operator ***op***(<br> unexpected_type<std::exception_ptr> const & x,<br> unexpected_type<std::exception_ptr> const & y ) |
|
||||
| Comparison with exception_ptr | nsel_P0323R <= 2 |
|
||||
| < > <= >= | constexpr bool operator ***op***(<br> unexpected_type<std::exception_ptr> const & x,<br> unexpected_type<std::exception_ptr> const & y ) |
|
||||
| Specialized algorithms | |
|
||||
| Make unexpected from | |
|
||||
|  Error | template<typename E><br>[constexpr] auto **make_unexpected**( E && v) -><br> unexpected_type< typename std::decay<E>::type>|
|
||||
| Make unexpected from | nsel_P0323R <= 3 |
|
||||
|  Current exception | [constexpr] auto **make_unexpected_from_current_exception**() -><br> unexpected_type< std::exception_ptr>|
|
||||
|
||||
<a id="comparison"></a>
|
||||
## Comparison with like types
|
||||
|
||||
|Feature |<br>std::pair|std:: optional |std:: expected |nonstd:: expected |Boost. Expected |Nonco expected |Andrei Expected |Hagan required |
|
||||
|----------------------|-------------|---------------|---------------|------------------|----------------|---------------|----------------|---------------|
|
||||
|More information | see [14] | see [5] | see [1] | this work | see [4] | see [7] | see [8] | see [13] |
|
||||
| | | | | | | | | |
|
||||
| C++03 | yes | no | no | no/not yet | no (union) | no | no | yes |
|
||||
| C++11 | yes | no | no | yes | yes | yes | yes | yes |
|
||||
| C++14 | yes | no | no | yes | yes | yes | yes | yes |
|
||||
| C++17 | yes | yes | no | yes | yes | yes | yes | yes |
|
||||
| | | | | | | | | |
|
||||
|DefaultConstructible | T param | yes | yes | yes | yes | no | no | no |
|
||||
|In-place construction | no | yes | yes | yes | yes | yes | no | no |
|
||||
|Literal type | yes | yes | yes | yes | yes | no | no | no |
|
||||
| | | | | | | | | |
|
||||
|Disengaged information| possible | no | yes | yes | yes | yes | yes | no |
|
||||
|Vary disengaged type | yes | no | yes | yes | yes | no | no | no |
|
||||
|Engaged nonuse throws | no | no | no | no | error_traits | no | no | yes |
|
||||
|Disengaged use throws | no | yes, value() | yes, value() | yes, value() | yes,<br>value()| yes,<br>get() | yes,<br>get() | n/a |
|
||||
| | | | | | | | | |
|
||||
|Proxy (rel.ops) | no | yes | yes | yes | yes | no | no | no |
|
||||
|References | no | yes | no/not yet | no/not yet | no/not yet | yes | no | no |
|
||||
|Chained visitor(s) | no | no | yes | maybe | yes | no | no | no |
|
||||
|
||||
Note 1: std::*experimental*::expected
|
||||
|
||||
Note 2: sources for [Nonco expected](https://github.com/martinmoene/spike-expected/tree/master/nonco), [Andrei Expected](https://github.com/martinmoene/spike-expected/tree/master/alexandrescu) and [Hagan required](https://github.com/martinmoene/spike-expected/tree/master/hagan) can befound in the [spike-expected](https://github.com/martinmoene/spike-expected) repository.
|
||||
|
||||
## Reported to work with
|
||||
|
||||
TBD
|
||||
|
||||
## Implementation notes
|
||||
|
||||
TBD
|
||||
|
||||
## Other implementations of expected
|
||||
|
||||
- Simon Brand. [C++11/14/17 std::expected with functional-style extensions](https://github.com/TartanLlama/expected). Single-header.
|
||||
- Isabella Muerte. [MNMLSTC Core](https://github.com/mnmlstc/core) (C++11).
|
||||
- Vicente J. Botet Escriba. [stdmake's expected](https://github.com/viboes/std-make/tree/master/include/experimental/fundamental/v3/expected) (C++17).
|
||||
- Facebook. [ Folly's Expected.h](https://github.com/facebook/folly/blob/master/folly/Expected.h) (C++14).
|
||||
|
||||
## Notes and references
|
||||
|
||||
[1] Vicente J. Botet Escriba. [p0323 - A proposal to add a utility class to represent expected object (latest)](http://wg21.link/p0323) (HTML). ([r10](http://wg21.link/p0323r10), [r9](http://wg21.link/p0323r9), [r8](http://wg21.link/p0323r8), [r7](http://wg21.link/p0323r7), [r6](http://wg21.link/p0323r6), [r5](http://wg21.link/p0323r5), [r4](http://wg21.link/p0323r4), [r3](http://wg21.link/p0323r3), [r2](http://wg21.link/p0323r2), [r1](http://wg21.link/n4109), [r0](http://wg21.link/n4015), [draft](https://github.com/viboes/std-make/blob/master/doc/proposal/expected/DXXXXR0_expected.pdf)).
|
||||
|
||||
[2] Vicente J. Botet Escriba. [JASEL: Just a simple experimental library for C++](https://github.com/viboes/std-make). Reference implementation of [expected](https://github.com/viboes/std-make/tree/master/include/experimental/fundamental/v3/expected).
|
||||
|
||||
[3] Vicente J. Botet Escriba. [Expected - An exception-friendly Error Monad](https://www.youtube.com/watch?v=Zdlt1rgYdMQ). C++Now 2014. 24 September 2014.
|
||||
|
||||
[4] Pierre Talbot. [Boost.Expected. Unofficial Boost candidate](http://www.google-melange.com/gsoc/proposal/review/google/gsoc2013/trademark/25002). 5 May 2013. [GitHub](https://github.com/TrademarkPewPew/Boost.Expected), [GSoC 2013 Proposal](http://www.google-melange.com/gsoc/proposal/review/google/gsoc2013/trademark/25002), [boost@lists.boost.org](http://permalink.gmane.org/gmane.comp.lib.boost.devel/240056 ).
|
||||
|
||||
[5] Fernando Cacciola and Andrzej Krzemieński. [A proposal to add a utility class to represent optional objects (Revision 4)](http://isocpp.org/files/papers/N3672.html). ISO/IEC JTC1 SC22 WG21 N3672 2013-04-19.
|
||||
|
||||
[6] Andrzej Krzemieński, [Optional library implementation in C++11](https://github.com/akrzemi1/Optional/).
|
||||
|
||||
[7] Anto Nonco. [Extending expected<T> to deal with references](http://anto-nonco.blogspot.nl/2013/03/extending-expected-to-deal-with.html). 27 May 2013.
|
||||
|
||||
[8] Andrei Alexandrescu. Systematic Error Handling in C++. Prepared for The C++and Beyond Seminar 2012. [Video](http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C). [Slides](http://sdrv.ms/RXjNPR).
|
||||
|
||||
[9] Andrei Alexandrescu. [Choose your Poison: Exceptions or Error Codes? (PDF)](http://accu.org/content/conf2007/Alexandrescu-Choose_Your_Poison.pdf). ACCU Conference 2007.
|
||||
|
||||
[10] Andrei Alexandrescu. [The Power of None (PPT)](http://nwcpp.org/static/talks/2006/The_Power_of_None.ppt). Northwest C++ Users' Group. [May 17th, 2006](http://nwcpp.org/may-2006.html).
|
||||
|
||||
[11] Jon Jagger. [A Return Type That Doesn't Like Being Ignored](http://accu.org/var/uploads/journals/overload53-FINAL.pdf#page=18). Overload issue 53, February 2003.
|
||||
|
||||
[12] Andrei Alexandrescu. [Error Handling in C++: Are we inching towards a total solution?](http://accu.org/index.php/conferences/2002/speakers2002). ACCU Conference 2002.
|
||||
|
||||
[13] Ken Hagan et al. [Exploding return codes](https://groups.google.com/d/msg/comp.lang.c++.moderated/BkZqPfoq3ys/H_PMR8Sat4oJ). comp.lang.c++.moderated. 11 February 2000.
|
||||
|
||||
[14] [std::pair](http://en.cppreference.com/w/cpp/utility/pair). cppreference.com
|
||||
|
||||
[15] Niall Douglas. [Outcome](https://ned14.github.io/outcome/). Very lightweight outcome<T> and result<T> (non-Boost edition).
|
||||
|
||||
[16] Niall Douglas. [p0762 - Concerns about expected<T, E> from the Boost.Outcome peer review](http://wg21.link/p0762). 15 October 2017.
|
||||
|
||||
## Appendix
|
||||
|
||||
### A.1 Compile-time information
|
||||
|
||||
The version of *expected lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`.
|
||||
|
||||
### A.2 Expected lite test specification
|
||||
|
||||
<details>
|
||||
<summary>click to expand</summary>
|
||||
<p>
|
||||
|
||||
```Text
|
||||
unexpected_type: Disallows default construction
|
||||
unexpected_type: Allows to copy-construct from unexpected_type, default
|
||||
unexpected_type: Allows to move-construct from unexpected_type, default
|
||||
unexpected_type: Allows to in-place-construct
|
||||
unexpected_type: Allows to in-place-construct from initializer_list
|
||||
unexpected_type: Allows to copy-construct from error_type
|
||||
unexpected_type: Allows to move-construct from error_type
|
||||
unexpected_type: Allows to copy-construct from unexpected_type, explicit converting
|
||||
unexpected_type: Allows to copy-construct from unexpected_type, non-explicit converting
|
||||
unexpected_type: Allows to move-construct from unexpected_type, explicit converting
|
||||
unexpected_type: Allows to move-construct from unexpected_type, non-explicit converting
|
||||
unexpected_type: Allows to copy-assign from unexpected_type, default
|
||||
unexpected_type: Allows to move-assign from unexpected_type, default
|
||||
unexpected_type: Allows to copy-assign from unexpected_type, converting
|
||||
unexpected_type: Allows to move-assign from unexpected, converting
|
||||
unexpected_type: Allows to observe its value via a l-value reference
|
||||
unexpected_type: Allows to observe its value via a r-value reference
|
||||
unexpected_type: Allows to modify its value via a l-value reference
|
||||
unexpected_type: Allows to be swapped
|
||||
unexpected_type<std::exception_ptr>: Disallows default construction
|
||||
unexpected_type<std::exception_ptr>: Allows to copy-construct from error_type
|
||||
unexpected_type<std::exception_ptr>: Allows to move-construct from error_type
|
||||
unexpected_type<std::exception_ptr>: Allows to copy-construct from an exception
|
||||
unexpected_type<std::exception_ptr>: Allows to observe its value
|
||||
unexpected_type<std::exception_ptr>: Allows to modify its value
|
||||
unexpected_type: Provides relational operators
|
||||
unexpected_type: Provides relational operators, std::exception_ptr specialization
|
||||
make_unexpected(): Allows to create an unexpected_type<E> from an E
|
||||
unexpected: C++17 and later provide unexpected_type as unexpected
|
||||
bad_expected_access: Disallows default construction
|
||||
bad_expected_access: Allows construction from error_type
|
||||
bad_expected_access: Allows to observe its error
|
||||
bad_expected_access: Allows to change its error
|
||||
bad_expected_access: Provides non-empty what()
|
||||
expected: Allows to default construct
|
||||
expected: Allows to copy-construct from expected: value
|
||||
expected: Allows to copy-construct from expected: error
|
||||
expected: Allows to move-construct from expected: value
|
||||
expected: Allows to move-construct from expected: error
|
||||
expected: Allows to copy-construct from expected; value, explicit converting
|
||||
expected: Allows to copy-construct from expected; error, explicit converting
|
||||
expected: Allows to copy-construct from expected; value, non-explicit converting
|
||||
expected: Allows to copy-construct from expected; error, non-explicit converting
|
||||
expected: Allows to move-construct from expected; value, explicit converting
|
||||
expected: Allows to move-construct from expected; error, explicit converting
|
||||
expected: Allows to move-construct from expected; value, non-explicit converting
|
||||
expected: Allows to move-construct from expected; error, non-explicit converting
|
||||
expected: Allows to forward-construct from value, explicit converting
|
||||
expected: Allows to forward-construct from value, non-explicit converting
|
||||
expected: Allows to in-place-construct value
|
||||
expected: Allows to in-place-construct value from initializer_list
|
||||
expected: Allows to copy-construct from unexpected, explicit converting
|
||||
expected: Allows to copy-construct from unexpected, non-explicit converting
|
||||
expected: Allows to move-construct from unexpected, explicit converting
|
||||
expected: Allows to move-construct from unexpected, non-explicit converting
|
||||
expected: Allows to in-place-construct error
|
||||
expected: Allows to in-place-construct error from initializer_list
|
||||
expected: Allows to copy-assign from expected, value
|
||||
expected: Allows to copy-assign from expected, error
|
||||
expected: Allows to move-assign from expected, value
|
||||
expected: Allows to move-assign from expected, error
|
||||
expected: Allows to forward-assign from value
|
||||
expected: Allows to copy-assign from unexpected
|
||||
expected: Allows to move-assign from unexpected
|
||||
expected: Allows to move-assign from move-only unexpected
|
||||
expected: Allows to emplace value
|
||||
expected: Allows to emplace value from initializer_list
|
||||
expected: Allows to be swapped
|
||||
expected: Allows to observe its value via a pointer
|
||||
expected: Allows to observe its value via a pointer to constant
|
||||
expected: Allows to modify its value via a pointer
|
||||
expected: Allows to observe its value via a l-value reference
|
||||
expected: Allows to observe its value via a r-value reference
|
||||
expected: Allows to modify its value via a l-value reference
|
||||
expected: Allows to modify its value via a r-value reference
|
||||
expected: Allows to observe if it contains a value (or error)
|
||||
expected: Allows to observe its value
|
||||
expected: Allows to modify its value
|
||||
expected: Allows to move its value
|
||||
expected: Allows to observe its error
|
||||
expected: Allows to modify its error
|
||||
expected: Allows to move its error
|
||||
expected: Allows to observe its error as unexpected
|
||||
expected: Allows to query if it contains an exception of a specific base type
|
||||
expected: Allows to observe its value if available, or obtain a specified value otherwise
|
||||
expected: Allows to move its value if available, or obtain a specified value otherwise
|
||||
expected: Throws bad_expected_access on value access when disengaged
|
||||
expected<void>: Allows to default-construct
|
||||
expected<void>: Allows to copy-construct from expected<void>: value
|
||||
expected<void>: Allows to copy-construct from expected<void>: error
|
||||
expected<void>: Allows to move-construct from expected<void>: value
|
||||
expected<void>: Allows to move-construct from expected<void>: error
|
||||
expected<void>: Allows to in-place-construct
|
||||
expected<void>: Allows to copy-construct from unexpected, explicit converting
|
||||
expected<void>: Allows to copy-construct from unexpected, non-explicit converting
|
||||
expected<void>: Allows to move-construct from unexpected, explicit converting
|
||||
expected<void>: Allows to move-construct from unexpected, non-explicit converting
|
||||
expected<void>: Allows to in-place-construct unexpected_type
|
||||
expected<void>: Allows to in-place-construct error from initializer_list
|
||||
expected<void>: Allows to copy-assign from expected, value
|
||||
expected<void>: Allows to copy-assign from expected, error
|
||||
expected<void>: Allows to move-assign from expected, value
|
||||
expected<void>: Allows to move-assign from expected, error
|
||||
expected<void>: Allows to emplace value
|
||||
expected<void>: Allows to be swapped
|
||||
expected<void>: Allows to observe if it contains a value (or error)
|
||||
expected<void>: Allows to observe its value
|
||||
expected<void>: Allows to observe its error
|
||||
expected<void>: Allows to modify its error
|
||||
expected<void>: Allows to move its error
|
||||
expected<void>: Allows to observe its error as unexpected
|
||||
expected<void>: Allows to query if it contains an exception of a specific base type
|
||||
expected<void>: Throws bad_expected_access on value access when disengaged
|
||||
operators: Provides expected relational operators
|
||||
swap: Allows expected to be swapped
|
||||
std::hash: Allows to compute hash value for expected
|
||||
tweak header: reads tweak header if supported [tweak]
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
71
cmake/external/glib/cppgir/expected-lite/appveyor.yml
vendored
Normal file
71
cmake/external/glib/cppgir/expected-lite/appveyor.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
version: "{branch} #{build}"
|
||||
|
||||
shallow_clone: true
|
||||
|
||||
image:
|
||||
- Visual Studio 2019
|
||||
- Visual Studio 2017
|
||||
- Visual Studio 2015
|
||||
|
||||
platform:
|
||||
- Win32
|
||||
- x64
|
||||
|
||||
configuration:
|
||||
- Debug
|
||||
- Release
|
||||
|
||||
build:
|
||||
parallel: true
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- generator: "Visual Studio 16 2019"
|
||||
select_sv: -DEXPECTED_LITE_OPT_SELECT_STD=ON
|
||||
- generator: "Visual Studio 16 2019"
|
||||
select_sv: -DEXPECTED_LITE_OPT_SELECT_NONSTD=ON
|
||||
- generator: "Visual Studio 15 2017"
|
||||
select_sv: -DEXPECTED_LITE_OPT_SELECT_STD=ON
|
||||
- generator: "Visual Studio 15 2017"
|
||||
select_sv: -DEXPECTED_LITE_OPT_SELECT_NONSTD=ON
|
||||
- generator: "Visual Studio 14 2015"
|
||||
# - generator: "Visual Studio 12 2013"
|
||||
# - generator: "Visual Studio 11 2012"
|
||||
# - generator: "Visual Studio 10 2010"
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- image: Visual Studio 2015
|
||||
generator: "Visual Studio 16 2019"
|
||||
- image: Visual Studio 2019
|
||||
generator: "Visual Studio 15 2017"
|
||||
- image: Visual Studio 2019
|
||||
generator: "Visual Studio 14 2015"
|
||||
- image: Visual Studio 2019
|
||||
generator: "Visual Studio 12 2013"
|
||||
- image: Visual Studio 2019
|
||||
generator: "Visual Studio 11 2012"
|
||||
- image: Visual Studio 2019
|
||||
generator: "Visual Studio 10 2010"
|
||||
- image: Visual Studio 2015
|
||||
generator: "Visual Studio 15 2017"
|
||||
- image: Visual Studio 2017
|
||||
generator: "Visual Studio 16 2019"
|
||||
- image: Visual Studio 2017
|
||||
generator: "Visual Studio 14 2015"
|
||||
- image: Visual Studio 2017
|
||||
generator: "Visual Studio 12 2013"
|
||||
- image: Visual Studio 2017
|
||||
generator: "Visual Studio 11 2012"
|
||||
- image: Visual Studio 2017
|
||||
generator: "Visual Studio 10 2010"
|
||||
|
||||
before_build:
|
||||
- mkdir build && cd build
|
||||
- cmake -A %platform% -G "%generator%" "%select_sv%" -DEXPECTED_LITE_OPT_BUILD_TESTS=ON -DEXPECTED_LITE_OPT_BUILD_EXAMPLES=OFF ..
|
||||
|
||||
build_script:
|
||||
- cmake --build . --config %configuration%
|
||||
|
||||
test_script:
|
||||
- ctest --output-on-failure -C %configuration%
|
||||
24
cmake/external/glib/cppgir/expected-lite/cmake/expected-lite-config-version.cmake.in
vendored
Normal file
24
cmake/external/glib/cppgir/expected-lite/cmake/expected-lite-config-version.cmake.in
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Adapted from write_basic_package_version_file(... COMPATIBILITY SameMajorVersion) output
|
||||
# ARCH_INDEPENDENT is only present in cmake 3.14 and onwards
|
||||
|
||||
set( PACKAGE_VERSION "@package_version@" )
|
||||
|
||||
if( PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION )
|
||||
set( PACKAGE_VERSION_COMPATIBLE FALSE )
|
||||
else()
|
||||
if( "@package_version@" MATCHES "^([0-9]+)\\." )
|
||||
set( CVF_VERSION_MAJOR "${CMAKE_MATCH_1}" )
|
||||
else()
|
||||
set( CVF_VERSION_MAJOR "@package_version@" )
|
||||
endif()
|
||||
|
||||
if( PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR )
|
||||
set( PACKAGE_VERSION_COMPATIBLE TRUE )
|
||||
else()
|
||||
set( PACKAGE_VERSION_COMPATIBLE FALSE )
|
||||
endif()
|
||||
|
||||
if( PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION )
|
||||
set( PACKAGE_VERSION_EXACT TRUE )
|
||||
endif()
|
||||
endif()
|
||||
7
cmake/external/glib/cppgir/expected-lite/cmake/expected-lite-config.cmake.in
vendored
Normal file
7
cmake/external/glib/cppgir/expected-lite/cmake/expected-lite-config.cmake.in
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
# Only include targets once:
|
||||
|
||||
if( NOT TARGET @package_name@::@package_name@ )
|
||||
include( "${CMAKE_CURRENT_LIST_DIR}/@package_target@.cmake" )
|
||||
endif()
|
||||
27
cmake/external/glib/cppgir/expected-lite/conanfile.py
vendored
Normal file
27
cmake/external/glib/cppgir/expected-lite/conanfile.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
from conans import ConanFile, CMake
|
||||
|
||||
class ExpectedLiteConan(ConanFile):
|
||||
version = "0.6.2"
|
||||
name = "expected-lite"
|
||||
description = "Expected objects for C++11 and later"
|
||||
license = "Boost Software License - Version 1.0. http://www.boost.org/LICENSE_1_0.txt"
|
||||
url = "https://github.com/martinmoene/expected-lite.git"
|
||||
exports_sources = "include/nonstd/*", "CMakeLists.txt", "cmake/*", "LICENSE.txt"
|
||||
settings = "compiler", "build_type", "arch"
|
||||
build_policy = "missing"
|
||||
author = "Martin Moene"
|
||||
|
||||
def build(self):
|
||||
"""Avoid warning on build step"""
|
||||
pass
|
||||
|
||||
def package(self):
|
||||
"""Run CMake install"""
|
||||
cmake = CMake(self)
|
||||
cmake.definitions["EXPECTED_LITE_OPT_BUILD_TESTS"] = "OFF"
|
||||
cmake.definitions["EXPECTED_LITE_OPT_BUILD_EXAMPLES"] = "OFF"
|
||||
cmake.configure()
|
||||
cmake.install()
|
||||
|
||||
def package_info(self):
|
||||
self.info.header_only()
|
||||
33
cmake/external/glib/cppgir/expected-lite/example/01-basic.cpp
vendored
Normal file
33
cmake/external/glib/cppgir/expected-lite/example/01-basic.cpp
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Convert text to number and yield expected with number or error text.
|
||||
|
||||
#include "nonstd/expected.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace nonstd;
|
||||
using namespace std::literals;
|
||||
|
||||
auto to_int( char const * const text ) -> expected<int, std::string>
|
||||
{
|
||||
char * pos = nullptr;
|
||||
auto value = strtol( text, &pos, 0 );
|
||||
|
||||
if ( pos != text ) return value;
|
||||
else return make_unexpected( "'"s + text + "' isn't a number" );
|
||||
}
|
||||
|
||||
int main( int argc, char * argv[] )
|
||||
{
|
||||
auto text = argc > 1 ? argv[1] : "42";
|
||||
|
||||
auto ei = to_int( text );
|
||||
|
||||
if ( ei ) std::cout << "'" << text << "' is " << *ei << ", ";
|
||||
else std::cout << "Error: " << ei.error();
|
||||
}
|
||||
|
||||
// cl -EHsc -wd4814 -I../include 01-basic.cpp && 01-basic.exe 123 && 01-basic.exe abc
|
||||
// g++ -std=c++14 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe 123 && 01-basic.exe abc
|
||||
// '123' is 123, Error: 'abc' isn't a number
|
||||
68
cmake/external/glib/cppgir/expected-lite/example/02-required.cpp
vendored
Normal file
68
cmake/external/glib/cppgir/expected-lite/example/02-required.cpp
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
// Use a non-ignorable value with expected.
|
||||
|
||||
#include "nonstd/expected.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace nonstd;
|
||||
|
||||
template< typename T >
|
||||
class required
|
||||
{
|
||||
public:
|
||||
required( T const & value)
|
||||
: content( value ) {}
|
||||
|
||||
required( required && other )
|
||||
: content( other.content )
|
||||
, ignored( other.ignored )
|
||||
{
|
||||
other.ignored = false;
|
||||
}
|
||||
|
||||
required( required const & other ) = delete;
|
||||
|
||||
~required() noexcept( false )
|
||||
{
|
||||
if ( ignored )
|
||||
throw std::runtime_error("required: content unobserved");
|
||||
};
|
||||
|
||||
T const & operator *() const { ignored = false; return content; }
|
||||
|
||||
private:
|
||||
T content;
|
||||
mutable bool ignored = true;
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
auto make_required( T value ) -> required<T>
|
||||
{
|
||||
return required<T>( std::move(value) );
|
||||
}
|
||||
|
||||
using unused_type = char;
|
||||
|
||||
auto produce( int value ) -> expected< required<int>, unused_type >
|
||||
{
|
||||
return make_required( std::move(value) );
|
||||
}
|
||||
|
||||
int main( int argc, char * argv[] )
|
||||
{
|
||||
try
|
||||
{
|
||||
auto er42 = produce( 42 );
|
||||
auto er13 = produce( 13 );
|
||||
|
||||
std::cout << "value: " << **er42 << "\n";
|
||||
}
|
||||
catch ( std::exception const & e )
|
||||
{
|
||||
std::cout << "Error: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
// cl -EHsc -wd4814 -Zc:implicitNoexcept- -I../include 02-required.cpp && 02-required.exe
|
||||
// g++ -std=c++14 -Wall -I../include -o 02-required.exe 02-required.cpp && 02-required.exe
|
||||
// value: 42
|
||||
// Error: required: content unobserved
|
||||
77
cmake/external/glib/cppgir/expected-lite/example/03-no-exceptions.cpp
vendored
Normal file
77
cmake/external/glib/cppgir/expected-lite/example/03-no-exceptions.cpp
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2016-2020 Martin Moene
|
||||
//
|
||||
// https://github.com/martinmoene/expected-lite
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "nonstd/expected.hpp"
|
||||
#include <iostream>
|
||||
|
||||
template< typename T >
|
||||
void use( T const & /*x*/) {}
|
||||
|
||||
#define expected_PRESENT( x ) \
|
||||
std::cout << #x << ": " << x << "\n"
|
||||
|
||||
#define expected_ABSENT( x ) \
|
||||
std::cout << #x << ": (undefined)\n"
|
||||
|
||||
void report()
|
||||
{
|
||||
#ifdef __cpp_exceptions
|
||||
expected_PRESENT( __cpp_exceptions );
|
||||
#else
|
||||
expected_ABSENT( __cpp_exceptions );
|
||||
#endif
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
expected_PRESENT( __EXCEPTIONS );
|
||||
#else
|
||||
expected_ABSENT( __EXCEPTIONS );
|
||||
#endif
|
||||
|
||||
#ifdef _HAS_EXCEPTIONS
|
||||
expected_PRESENT( _HAS_EXCEPTIONS );
|
||||
#else
|
||||
expected_ABSENT( _HAS_EXCEPTIONS );
|
||||
#endif
|
||||
|
||||
#ifdef _CPPUNWIND
|
||||
expected_PRESENT( _CPPUNWIND );
|
||||
#else
|
||||
expected_ABSENT( _CPPUNWIND );
|
||||
#endif
|
||||
}
|
||||
|
||||
int violate_access()
|
||||
{
|
||||
nonstd::expected<int, char> eu( nonstd:: make_unexpected('a') );
|
||||
|
||||
return eu.value();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
report();
|
||||
|
||||
#if ! nsel_CONFIG_NO_EXCEPTIONS_SEH
|
||||
return violate_access();
|
||||
#else
|
||||
__try
|
||||
{
|
||||
return violate_access();
|
||||
}
|
||||
__except( EXCEPTION_EXECUTE_HANDLER )
|
||||
{
|
||||
std::cerr << "\n*** Executing SEH __except block ***\n";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// -Dnsel_CONFIG_NO_EXCEPTIONS=1 automatically determined in expected.hpp
|
||||
// -Dnsel_CONFIG_NO_EXCEPTIONS_SEH=0 default:1 for msvc
|
||||
|
||||
// cl -nologo -kernel -EHs-c- -GR- -I../include 03-no-exceptions.cpp && 03-no-exceptions
|
||||
// cl -nologo -kernel -EHs-c- -GR- -Dnsel_CONFIG_NO_EXCEPTIONS_SEH=0 -I../include 03-no-exceptions.cpp && 03-no-exceptions
|
||||
// g++ -Wall -fno-exceptions -I../include -o 03-no-exceptions 03-no-exceptions.cpp && 03-no-exceptions
|
||||
79
cmake/external/glib/cppgir/expected-lite/example/CMakeLists.txt
vendored
Normal file
79
cmake/external/glib/cppgir/expected-lite/example/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
# Copyright (c) 2016-2022 Martin Moene.
|
||||
#
|
||||
# https://github.com/martinmoene/expected-lite
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if( NOT DEFINED CMAKE_MINIMUM_REQUIRED_VERSION )
|
||||
cmake_minimum_required( VERSION 3.8 FATAL_ERROR )
|
||||
endif()
|
||||
|
||||
project( example LANGUAGES CXX )
|
||||
|
||||
set( unit_name "expected" )
|
||||
set( PACKAGE ${unit_name}-lite )
|
||||
|
||||
message( STATUS "Subproject '${PROJECT_NAME}', various examples")
|
||||
|
||||
# Target default options and definitions:
|
||||
|
||||
set( OPTIONS "" )
|
||||
#set( DEFINITIONS "" )
|
||||
|
||||
# Sources (.cpp), normal and no-exception, and their base names:
|
||||
|
||||
set( SOURCES_CPP11
|
||||
02-required.cpp
|
||||
)
|
||||
|
||||
set( SOURCES_CPP14
|
||||
01-basic.cpp
|
||||
)
|
||||
|
||||
# note: here variable must be quoted to create semicolon separated list:
|
||||
|
||||
string( REPLACE ".cpp" "" BASENAMES_CPP11 "${SOURCES_CPP11}" )
|
||||
string( REPLACE ".cpp" "" BASENAMES_CPP14 "${SOURCES_CPP14}" )
|
||||
|
||||
set( TARGETS_CPP11 ${BASENAMES_CPP11} )
|
||||
set( TARGETS_CPP14 ${BASENAMES_CPP14} )
|
||||
set( TARGETS_ALL ${TARGETS_CPP11} ${TARGETS_CPP14} )
|
||||
|
||||
# add targets:
|
||||
|
||||
foreach( name ${TARGETS_ALL} )
|
||||
add_executable( ${name} ${name}.cpp )
|
||||
target_link_libraries( ${name} PRIVATE ${PACKAGE} )
|
||||
endforeach()
|
||||
|
||||
# set compiler options:
|
||||
|
||||
if( ${CMAKE_GENERATOR} MATCHES Visual )
|
||||
foreach( name ${TARGETS_ALL} )
|
||||
target_compile_options( ${name} PUBLIC -W3 -EHsc -wd4814 -Zc:implicitNoexcept- )
|
||||
endforeach()
|
||||
else()
|
||||
foreach( name ${TARGETS_ALL} )
|
||||
target_compile_options( ${name} PUBLIC -Wall )
|
||||
endforeach()
|
||||
|
||||
foreach( name ${TARGETS_CPP11} )
|
||||
target_compile_options( ${name} PUBLIC -std=c++11 )
|
||||
endforeach()
|
||||
|
||||
foreach( name ${TARGETS_CPP14} )
|
||||
target_compile_options( ${name} PUBLIC -std=c++14 )
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# configure unit tests via CTest:
|
||||
|
||||
enable_testing()
|
||||
|
||||
foreach( name ${TARGETS_ALL} )
|
||||
add_test ( NAME ${name} COMMAND ${name} )
|
||||
set_property( TEST ${name} PROPERTY LABELS example )
|
||||
endforeach()
|
||||
|
||||
# end of file
|
||||
2520
cmake/external/glib/cppgir/expected-lite/include/nonstd/expected.hpp
vendored
Normal file
2520
cmake/external/glib/cppgir/expected-lite/include/nonstd/expected.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
63
cmake/external/glib/cppgir/expected-lite/project/CodeBlocks/expected-lite.cbp
vendored
Normal file
63
cmake/external/glib/cppgir/expected-lite/project/CodeBlocks/expected-lite.cbp
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="Expected Lite" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Release">
|
||||
<Option output="bin/Release/optional3" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj/Release/" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-O2" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
</Linker>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add option="-Wall" />
|
||||
</Compiler>
|
||||
<Unit filename="../../.gitattributes" />
|
||||
<Unit filename="../../.gitignore" />
|
||||
<Unit filename="../../.travis.yml" />
|
||||
<Unit filename="../../CHANGES.txt" />
|
||||
<Unit filename="../../CMakeLists.txt" />
|
||||
<Unit filename="../../LICENSE.txt" />
|
||||
<Unit filename="../../Notes.md" />
|
||||
<Unit filename="../../README.md" />
|
||||
<Unit filename="../../appveyor.yml" />
|
||||
<Unit filename="../../cmake/expected-lite-config-version.cmake.in" />
|
||||
<Unit filename="../../cmake/expected-lite-config.cmake.in" />
|
||||
<Unit filename="../../conanfile.py" />
|
||||
<Unit filename="../../example/01-basic.cpp" />
|
||||
<Unit filename="../../example/02-required.cpp" />
|
||||
<Unit filename="../../example/CMakeLists.txt" />
|
||||
<Unit filename="../../include/nonstd/expected.hpp" />
|
||||
<Unit filename="../../script/create-cov-rpt.py" />
|
||||
<Unit filename="../../script/create-vcpkg.py" />
|
||||
<Unit filename="../../script/update-version.py" />
|
||||
<Unit filename="../../script/upload-conan.py" />
|
||||
<Unit filename="../../test/CMakeLists.txt" />
|
||||
<Unit filename="../../test/expected-main.t.cpp" />
|
||||
<Unit filename="../../test/expected-main.t.hpp" />
|
||||
<Unit filename="../../test/expected.t.cpp" />
|
||||
<Unit filename="../../test/lest.hpp" />
|
||||
<Unit filename="../../test/odr.cpp" />
|
||||
<Unit filename="../../test/t-odr.bat" />
|
||||
<Unit filename="../../test/t.bat" />
|
||||
<Unit filename="../../test/tc.bat" />
|
||||
<Unit filename="../../test/tg-all.bat" />
|
||||
<Unit filename="../../test/tg.bat" />
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<envvars />
|
||||
<debugger />
|
||||
<lib_finder disable_auto="1" />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
9
cmake/external/glib/cppgir/expected-lite/project/CodeBlocks/expected-lite.workspace
vendored
Normal file
9
cmake/external/glib/cppgir/expected-lite/project/CodeBlocks/expected-lite.workspace
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_workspace_file>
|
||||
<Workspace title="Workspace">
|
||||
<Project filename="expected.cbp" />
|
||||
<Project filename="../../optional-lite/project/optional.cbp" />
|
||||
<Project filename="../../expected-spike/codeblocks.cbp" />
|
||||
<Project filename="../../../GitHub-Pre/IsoCpp/gsl-lite/project/gsl-lite.cbp" />
|
||||
</Workspace>
|
||||
</CodeBlocks_workspace_file>
|
||||
114
cmake/external/glib/cppgir/expected-lite/script/create-cov-rpt.py
vendored
Normal file
114
cmake/external/glib/cppgir/expected-lite/script/create-cov-rpt.py
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2019-2019 by Martin Moene
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
# script/create-cov-rpt.py, Python 3.4 and later
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
# Configuration:
|
||||
|
||||
cfg_github_project = 'expected-lite'
|
||||
cfg_github_user = 'martinmoene'
|
||||
cfg_prj_folder_level = 3
|
||||
|
||||
tpl_coverage_cmd = 'opencppcoverage --no_aggregate_by_file --sources {src} -- {exe}'
|
||||
|
||||
# End configuration.
|
||||
|
||||
def project_folder( f, args ):
|
||||
"""Project root"""
|
||||
if args.prj_folder:
|
||||
return args.prj_folder
|
||||
return os.path.normpath( os.path.join( os.path.dirname( os.path.abspath(f) ), '../' * args.prj_folder_level ) )
|
||||
|
||||
def executable_folder( f ):
|
||||
"""Folder where the xecutable is"""
|
||||
return os.path.dirname( os.path.abspath(f) )
|
||||
|
||||
def executable_name( f ):
|
||||
"""Folder where the executable is"""
|
||||
return os.path.basename( f )
|
||||
|
||||
def createCoverageReport( f, args ):
|
||||
print( "Creating coverage report for project '{usr}/{prj}', '{file}':".
|
||||
format( usr=args.user, prj=args.project, file=f ) )
|
||||
cmd = tpl_coverage_cmd.format( folder=executable_folder(f), src=project_folder(f, args), exe=executable_name(f) )
|
||||
if args.verbose:
|
||||
print( "> {}".format(cmd) )
|
||||
if not args.dry_run:
|
||||
os.chdir( executable_folder(f) )
|
||||
subprocess.call( cmd, shell=False )
|
||||
os.chdir( project_folder(f, args) )
|
||||
|
||||
def createCoverageReports( args ):
|
||||
for f in args.executable:
|
||||
createCoverageReport( f, args )
|
||||
|
||||
def createCoverageReportFromCommandLine():
|
||||
"""Collect arguments from the commandline and create coverage report."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Create coverage report.',
|
||||
epilog="""""",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
parser.add_argument(
|
||||
'executable',
|
||||
metavar='executable',
|
||||
type=str,
|
||||
nargs=1,
|
||||
help='executable to report on')
|
||||
|
||||
parser.add_argument(
|
||||
'-n', '--dry-run',
|
||||
action='store_true',
|
||||
help='do not execute conan commands')
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose',
|
||||
action='count',
|
||||
default=0,
|
||||
help='level of progress reporting')
|
||||
|
||||
parser.add_argument(
|
||||
'--user',
|
||||
metavar='u',
|
||||
type=str,
|
||||
default=cfg_github_user,
|
||||
help='github user name')
|
||||
|
||||
parser.add_argument(
|
||||
'--project',
|
||||
metavar='p',
|
||||
type=str,
|
||||
default=cfg_github_project,
|
||||
help='github project name')
|
||||
|
||||
parser.add_argument(
|
||||
'--prj-folder',
|
||||
metavar='f',
|
||||
type=str,
|
||||
default=None,
|
||||
help='project root folder')
|
||||
|
||||
parser.add_argument(
|
||||
'--prj-folder-level',
|
||||
metavar='n',
|
||||
type=int,
|
||||
default=cfg_prj_folder_level,
|
||||
help='project root folder level from executable')
|
||||
|
||||
createCoverageReports( parser.parse_args() )
|
||||
|
||||
if __name__ == '__main__':
|
||||
createCoverageReportFromCommandLine()
|
||||
|
||||
# end of file
|
||||
223
cmake/external/glib/cppgir/expected-lite/script/create-vcpkg.py
vendored
Normal file
223
cmake/external/glib/cppgir/expected-lite/script/create-vcpkg.py
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2019-2019 by Martin Moene
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
# script/upload-conan.py, Python 3.4 and later
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
# Configuration:
|
||||
|
||||
cfg_github_project = 'expected-lite'
|
||||
cfg_github_user = 'martinmoene'
|
||||
cfg_description = '(unused)'
|
||||
|
||||
cfg_cmakelists = 'CMakeLists.txt'
|
||||
cfg_readme = 'Readme.md'
|
||||
cfg_license = 'LICENSE.txt'
|
||||
cfg_ref_prefix = 'v'
|
||||
|
||||
cfg_sha512 = 'dadeda'
|
||||
cfg_vcpkg_description = '(no description found)'
|
||||
cfg_vcpkg_root = os.environ['VCPKG_ROOT']
|
||||
|
||||
cfg_cmake_optpfx = "EXPECTED_LITE"
|
||||
|
||||
# End configuration.
|
||||
|
||||
# vcpkg control and port templates:
|
||||
|
||||
tpl_path_vcpkg_control = '{vcpkg}/ports/{prj}/CONTROL'
|
||||
tpl_path_vcpkg_portfile = '{vcpkg}/ports/{prj}/portfile.cmake'
|
||||
|
||||
tpl_vcpkg_control =\
|
||||
"""Source: {prj}
|
||||
Version: {ver}
|
||||
Description: {desc}"""
|
||||
|
||||
tpl_vcpkg_portfile =\
|
||||
"""include(vcpkg_common_functions)
|
||||
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO {usr}/{prj}
|
||||
REF {ref}
|
||||
SHA512 {sha}
|
||||
)
|
||||
|
||||
vcpkg_configure_cmake(
|
||||
SOURCE_PATH ${{SOURCE_PATH}}
|
||||
PREFER_NINJA
|
||||
OPTIONS
|
||||
-D{optpfx}_OPT_BUILD_TESTS=OFF
|
||||
-D{optpfx}_OPT_BUILD_EXAMPLES=OFF
|
||||
)
|
||||
|
||||
vcpkg_install_cmake()
|
||||
|
||||
vcpkg_fixup_cmake_targets(
|
||||
CONFIG_PATH lib/cmake/${{PORT}}
|
||||
)
|
||||
|
||||
file(REMOVE_RECURSE
|
||||
${{CURRENT_PACKAGES_DIR}}/debug
|
||||
${{CURRENT_PACKAGES_DIR}}/lib
|
||||
)
|
||||
|
||||
file(INSTALL
|
||||
${{SOURCE_PATH}}/{lic} DESTINATION ${{CURRENT_PACKAGES_DIR}}/share/${{PORT}} RENAME copyright
|
||||
)"""
|
||||
|
||||
tpl_vcpkg_note_sha =\
|
||||
"""
|
||||
Next actions:
|
||||
- Obtain package SHA: 'vcpkg install {prj}', copy SHA mentioned in 'Actual hash: [...]'
|
||||
- Add SHA to package: 'script\create-vcpkg --sha={sha}'
|
||||
- Install package : 'vcpkg install {prj}'"""
|
||||
|
||||
tpl_vcpkg_note_install =\
|
||||
"""
|
||||
Next actions:
|
||||
- Install package: 'vcpkg install {prj}'"""
|
||||
|
||||
# End of vcpkg templates
|
||||
|
||||
def versionFrom( filename ):
|
||||
"""Obtain version from CMakeLists.txt"""
|
||||
with open( filename, 'r' ) as f:
|
||||
content = f.read()
|
||||
version = re.search(r'VERSION\s(\d+\.\d+\.\d+)', content).group(1)
|
||||
return version
|
||||
|
||||
def descriptionFrom( filename ):
|
||||
"""Obtain description from CMakeLists.txt"""
|
||||
with open( filename, 'r' ) as f:
|
||||
content = f.read()
|
||||
description = re.search(r'DESCRIPTION\s"(.*)"', content).group(1)
|
||||
return description if description else cfg_vcpkg_description
|
||||
|
||||
def vcpkgRootFrom( path ):
|
||||
return path if path else './vcpkg'
|
||||
|
||||
def to_ref( version ):
|
||||
"""Add prefix to version/tag, like v1.2.3"""
|
||||
return cfg_ref_prefix + version
|
||||
|
||||
def control_path( args ):
|
||||
"""Create path like vcpks/ports/_project_/CONTROL"""
|
||||
return tpl_path_vcpkg_control.format( vcpkg=args.vcpkg_root, prj=args.project )
|
||||
|
||||
def portfile_path( args ):
|
||||
"""Create path like vcpks/ports/_project_/portfile.cmake"""
|
||||
return tpl_path_vcpkg_portfile.format( vcpkg=args.vcpkg_root, prj=args.project )
|
||||
|
||||
def createControl( args ):
|
||||
"""Create vcpkg CONTROL file"""
|
||||
output = tpl_vcpkg_control.format(
|
||||
prj=args.project, ver=args.version, desc=args.description )
|
||||
if args.verbose:
|
||||
print( "Creating control file '{f}':".format( f=control_path( args ) ) )
|
||||
if args.verbose > 1:
|
||||
print( output )
|
||||
os.makedirs( os.path.dirname( control_path( args ) ), exist_ok=True )
|
||||
with open( control_path( args ), 'w') as f:
|
||||
print( output, file=f )
|
||||
|
||||
def createPortfile( args ):
|
||||
"""Create vcpkg portfile"""
|
||||
output = tpl_vcpkg_portfile.format(
|
||||
optpfx=cfg_cmake_optpfx, usr=args.user, prj=args.project, ref=to_ref(args.version), sha=args.sha, lic=cfg_license )
|
||||
if args.verbose:
|
||||
print( "Creating portfile '{f}':".format( f=portfile_path( args ) ) )
|
||||
if args.verbose > 1:
|
||||
print( output )
|
||||
os.makedirs( os.path.dirname( portfile_path( args ) ), exist_ok=True )
|
||||
with open( portfile_path( args ), 'w') as f:
|
||||
print( output, file=f )
|
||||
|
||||
def printNotes( args ):
|
||||
if args.sha == cfg_sha512:
|
||||
print( tpl_vcpkg_note_sha.
|
||||
format( prj=args.project, sha='...' ) )
|
||||
else:
|
||||
print( tpl_vcpkg_note_install.
|
||||
format( prj=args.project ) )
|
||||
|
||||
def createVcpkg( args ):
|
||||
print( "Creating vcpkg for '{usr}/{prj}', version '{ver}' in folder '{vcpkg}':".
|
||||
format( usr=args.user, prj=args.project, ver=args.version, vcpkg=args.vcpkg_root, ) )
|
||||
createControl( args )
|
||||
createPortfile( args )
|
||||
printNotes( args )
|
||||
|
||||
def createVcpkgFromCommandLine():
|
||||
"""Collect arguments from the commandline and create vcpkg."""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Create microsoft vcpkg.',
|
||||
epilog="""""",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose',
|
||||
action='count',
|
||||
default=0,
|
||||
help='level of progress reporting')
|
||||
|
||||
parser.add_argument(
|
||||
'--user',
|
||||
metavar='u',
|
||||
type=str,
|
||||
default=cfg_github_user,
|
||||
help='github user name')
|
||||
|
||||
parser.add_argument(
|
||||
'--project',
|
||||
metavar='p',
|
||||
type=str,
|
||||
default=cfg_github_project,
|
||||
help='github project name')
|
||||
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
metavar='d',
|
||||
type=str,
|
||||
# default=cfg_description,
|
||||
default=descriptionFrom( cfg_cmakelists ),
|
||||
help='vcpkg description [from ' + cfg_cmakelists + ']')
|
||||
|
||||
parser.add_argument(
|
||||
'--version',
|
||||
metavar='v',
|
||||
type=str,
|
||||
default=versionFrom( cfg_cmakelists ),
|
||||
help='version number [from ' + cfg_cmakelists + ']')
|
||||
|
||||
parser.add_argument(
|
||||
'--sha',
|
||||
metavar='s',
|
||||
type=str,
|
||||
default=cfg_sha512,
|
||||
help='sha of package')
|
||||
|
||||
parser.add_argument(
|
||||
'--vcpkg-root',
|
||||
metavar='r',
|
||||
type=str,
|
||||
default=vcpkgRootFrom( cfg_vcpkg_root ),
|
||||
help='parent folder containing ports to write files to')
|
||||
|
||||
createVcpkg( parser.parse_args() )
|
||||
|
||||
if __name__ == '__main__':
|
||||
createVcpkgFromCommandLine()
|
||||
|
||||
# end of file
|
||||
129
cmake/external/glib/cppgir/expected-lite/script/update-version.py
vendored
Normal file
129
cmake/external/glib/cppgir/expected-lite/script/update-version.py
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2017-2018 by Martin Moene
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
# script/update-version.py
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Configuration:
|
||||
|
||||
table = (
|
||||
# path, substitute find, substitute format
|
||||
( 'CMakeLists.txt'
|
||||
, r'\W{2,4}VERSION\W+([0-9]+\.[0-9]+\.[0-9]+)\W*$'
|
||||
, ' VERSION {major}.{minor}.{patch}' )
|
||||
|
||||
, ( 'CMakeLists.txt'
|
||||
, r'set\W+expected_lite_version\W+"([0-9]+\.[0-9]+\.[0-9]+)"\W+$'
|
||||
, 'set( expected_lite_version "{major}.{minor}.{patch}" )\n' )
|
||||
|
||||
# , ( 'example/cmake-pkg/CMakeLists.txt'
|
||||
# , r'set\W+expected_lite_version\W+"([0-9]+\.[0-9]+(\.[0-9]+)?)"\W+$'
|
||||
# , 'set( expected_lite_version "{major}.{minor}" )\n' )
|
||||
#
|
||||
# , ( 'script/install-xxx-pkg.py'
|
||||
# , r'\expected_lite_version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$'
|
||||
# , 'expected_lite_version = "{major}.{minor}.{patch}"\n' )
|
||||
|
||||
, ( 'conanfile.py'
|
||||
, r'version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$'
|
||||
, 'version = "{major}.{minor}.{patch}"' )
|
||||
|
||||
, ( 'include/nonstd/expected.hpp'
|
||||
, r'\#define\s+expected_lite_MAJOR\s+[0-9]+\s*$'
|
||||
, '#define expected_lite_MAJOR {major}' )
|
||||
|
||||
, ( 'include/nonstd/expected.hpp'
|
||||
, r'\#define\s+expected_lite_MINOR\s+[0-9]+\s*$'
|
||||
, '#define expected_lite_MINOR {minor}' )
|
||||
|
||||
, ( 'include/nonstd/expected.hpp'
|
||||
, r'\#define\s+expected_lite_PATCH\s+[0-9]+\s*$'
|
||||
, '#define expected_lite_PATCH {patch}\n' )
|
||||
)
|
||||
|
||||
# End configuration.
|
||||
|
||||
def readFile( in_path ):
|
||||
"""Return content of file at given path"""
|
||||
with open( in_path, 'r' ) as in_file:
|
||||
contents = in_file.read()
|
||||
return contents
|
||||
|
||||
def writeFile( out_path, contents ):
|
||||
"""Write contents to file at given path"""
|
||||
with open( out_path, 'w' ) as out_file:
|
||||
out_file.write( contents )
|
||||
|
||||
def replaceFile( output_path, input_path ):
|
||||
# prevent race-condition (Python 3.3):
|
||||
if sys.version_info >= (3, 3):
|
||||
os.replace( output_path, input_path )
|
||||
else:
|
||||
os.remove( input_path )
|
||||
os.rename( output_path, input_path )
|
||||
|
||||
def editFileToVersion( version, info, verbose ):
|
||||
"""Update version given file path, version regexp and new version format in info"""
|
||||
major, minor, patch = version.split('.')
|
||||
in_path, ver_re, ver_fmt = info
|
||||
out_path = in_path + '.tmp'
|
||||
new_text = ver_fmt.format( major=major, minor=minor, patch=patch )
|
||||
|
||||
if verbose:
|
||||
print( "- {path} => '{text}':".format( path=in_path, text=new_text.strip('\n') ) )
|
||||
|
||||
writeFile(
|
||||
out_path,
|
||||
re.sub(
|
||||
ver_re, new_text, readFile( in_path )
|
||||
, count=0, flags=re.MULTILINE
|
||||
)
|
||||
)
|
||||
replaceFile( out_path, in_path )
|
||||
|
||||
def editFilesToVersion( version, table, verbose ):
|
||||
if verbose:
|
||||
print( "Editing files to version {v}:".format(v=version) )
|
||||
for item in table:
|
||||
editFileToVersion( version, item, verbose )
|
||||
|
||||
def editFilesToVersionFromCommandLine():
|
||||
"""Update version number given on command line in paths from configuration table."""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Update version number in files.',
|
||||
epilog="""""",
|
||||
formatter_class=argparse.RawTextHelpFormatter)
|
||||
|
||||
parser.add_argument(
|
||||
'version',
|
||||
metavar='version',
|
||||
type=str,
|
||||
nargs=1,
|
||||
help='new version number, like 1.2.3')
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose',
|
||||
action='store_true',
|
||||
help='report the name of the file being processed')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
editFilesToVersion( args.version[0], table, args.verbose )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
editFilesToVersionFromCommandLine()
|
||||
|
||||
# end of file
|
||||
114
cmake/external/glib/cppgir/expected-lite/script/upload-conan.py
vendored
Normal file
114
cmake/external/glib/cppgir/expected-lite/script/upload-conan.py
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2019-2019 by Martin Moene
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
# script/upload-conan.py
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
# Configuration:
|
||||
|
||||
def_conan_project = 'expected-lite'
|
||||
def_conan_user = 'nonstd-lite'
|
||||
def_conan_channel = 'stable'
|
||||
cfg_conanfile = 'conanfile.py'
|
||||
|
||||
tpl_conan_create = 'conan create . {usr}/{chn}'
|
||||
tpl_conan_upload = 'conan upload --remote {usr} {prj}/{ver}@{usr}/{chn}'
|
||||
|
||||
# End configuration.
|
||||
|
||||
def versionFrom( filename ):
|
||||
"""Obtain version from conanfile.py"""
|
||||
with open( filename ) as f:
|
||||
content = f.read()
|
||||
version = re.search(r'version\s=\s"(.*)"', content).group(1)
|
||||
return version
|
||||
|
||||
def createConanPackage( args ):
|
||||
"""Create conan package and upload it."""
|
||||
cmd = tpl_conan_create.format(usr=args.user, chn=args.channel)
|
||||
if args.verbose:
|
||||
print( "> {}".format(cmd) )
|
||||
if not args.dry_run:
|
||||
subprocess.call( cmd, shell=False )
|
||||
|
||||
def uploadConanPackage( args ):
|
||||
"""Create conan package and upload it."""
|
||||
cmd = tpl_conan_upload.format(prj=args.project, usr=args.user, chn=args.channel, ver=args.version)
|
||||
if args.verbose:
|
||||
print( "> {}".format(cmd) )
|
||||
if not args.dry_run:
|
||||
subprocess.call( cmd, shell=False )
|
||||
|
||||
def uploadToConan( args ):
|
||||
"""Create conan package and upload it."""
|
||||
print( "Updating project '{prj}' to user '{usr}', channel '{chn}', version {ver}:".
|
||||
format(prj=args.project, usr=args.user, chn=args.channel, ver=args.version) )
|
||||
createConanPackage( args )
|
||||
uploadConanPackage( args )
|
||||
|
||||
def uploadToConanFromCommandLine():
|
||||
"""Collect arguments from the commandline and create conan package and upload it."""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Create conan package and upload it to conan.',
|
||||
epilog="""""",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
parser.add_argument(
|
||||
'-n', '--dry-run',
|
||||
action='store_true',
|
||||
help='do not execute conan commands')
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose',
|
||||
action='count',
|
||||
default=0,
|
||||
help='level of progress reporting')
|
||||
|
||||
parser.add_argument(
|
||||
'--project',
|
||||
metavar='p',
|
||||
type=str,
|
||||
default=def_conan_project,
|
||||
help='conan project')
|
||||
|
||||
parser.add_argument(
|
||||
'--user',
|
||||
metavar='u',
|
||||
type=str,
|
||||
default=def_conan_user,
|
||||
help='conan user')
|
||||
|
||||
parser.add_argument(
|
||||
'--channel',
|
||||
metavar='c',
|
||||
type=str,
|
||||
default=def_conan_channel,
|
||||
help='conan channel')
|
||||
|
||||
parser.add_argument(
|
||||
'--version',
|
||||
metavar='v',
|
||||
type=str,
|
||||
default=versionFrom( cfg_conanfile ),
|
||||
help='version number [from conanfile.py]')
|
||||
|
||||
uploadToConan( parser.parse_args() )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
uploadToConanFromCommandLine()
|
||||
|
||||
# end of file
|
||||
233
cmake/external/glib/cppgir/expected-lite/test/CMakeLists.txt
vendored
Normal file
233
cmake/external/glib/cppgir/expected-lite/test/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
# Copyright 2016-2018 by Martin Moene
|
||||
#
|
||||
# https://github.com/martinmoene/expected-lite
|
||||
#
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if( NOT DEFINED CMAKE_MINIMUM_REQUIRED_VERSION )
|
||||
cmake_minimum_required( VERSION 3.5 FATAL_ERROR )
|
||||
endif()
|
||||
|
||||
project( test LANGUAGES CXX )
|
||||
|
||||
set( unit_name "expected" )
|
||||
set( PACKAGE ${unit_name}-lite )
|
||||
set( PROGRAM ${unit_name}-lite )
|
||||
set( SOURCES ${unit_name}-main.t.cpp ${unit_name}.t.cpp )
|
||||
set( TWEAKD "." )
|
||||
|
||||
message( STATUS "Subproject '${PROJECT_NAME}', programs '${PROGRAM}-*'")
|
||||
|
||||
set( OPTIONS "" )
|
||||
set( DEFCMN "-Dlest_FEATURE_AUTO_REGISTER=1" )
|
||||
|
||||
set( HAS_STD_FLAGS FALSE )
|
||||
set( HAS_CPP98_FLAG FALSE )
|
||||
set( HAS_CPP11_FLAG FALSE )
|
||||
set( HAS_CPP14_FLAG FALSE )
|
||||
set( HAS_CPP17_FLAG FALSE )
|
||||
set( HAS_CPP20_FLAG FALSE )
|
||||
set( HAS_CPPLATEST_FLAG FALSE )
|
||||
|
||||
if ( EXPEXTED_P0323R LESS "99" )
|
||||
set( DEFCMN ${DEFCMN} "-Dnsel_P0323R=${EXPEXTED_P0323R}" )
|
||||
endif()
|
||||
|
||||
if( MSVC )
|
||||
message( STATUS "Matched: MSVC")
|
||||
|
||||
set( HAS_STD_FLAGS TRUE )
|
||||
|
||||
set( OPTIONS -W3 -EHsc )
|
||||
set( DEFINITIONS -D_SCL_SECURE_NO_WARNINGS ${DEFCMN} )
|
||||
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.00 )
|
||||
set( HAS_CPP14_FLAG TRUE )
|
||||
set( HAS_CPPLATEST_FLAG TRUE )
|
||||
endif()
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.11 )
|
||||
set( HAS_CPP17_FLAG TRUE )
|
||||
endif()
|
||||
|
||||
elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" )
|
||||
message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'")
|
||||
|
||||
set( HAS_STD_FLAGS TRUE )
|
||||
set( HAS_CPP98_FLAG TRUE )
|
||||
|
||||
set( OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wno-missing-braces -fno-elide-constructors )
|
||||
set( DEFINITIONS ${DEFCMN} )
|
||||
|
||||
# GNU: available -std flags depends on version
|
||||
if( CMAKE_CXX_COMPILER_ID MATCHES "GNU" )
|
||||
message( STATUS "Matched: GNU")
|
||||
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.0 )
|
||||
set( HAS_CPP11_FLAG TRUE )
|
||||
endif()
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.2 )
|
||||
set( HAS_CPP14_FLAG TRUE )
|
||||
endif()
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.1.0 )
|
||||
set( HAS_CPP17_FLAG TRUE )
|
||||
endif()
|
||||
|
||||
# AppleClang: available -std flags depends on version
|
||||
elseif( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" )
|
||||
message( STATUS "Matched: AppleClang")
|
||||
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 )
|
||||
set( HAS_CPP11_FLAG TRUE )
|
||||
endif()
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1.0 )
|
||||
set( HAS_CPP14_FLAG TRUE )
|
||||
endif()
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.2.0 )
|
||||
set( HAS_CPP17_FLAG TRUE )
|
||||
endif()
|
||||
|
||||
# Clang: available -std flags depends on version
|
||||
elseif( CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
|
||||
message( STATUS "Matched: Clang")
|
||||
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3.0 )
|
||||
set( HAS_CPP11_FLAG TRUE )
|
||||
endif()
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4.0 )
|
||||
set( HAS_CPP14_FLAG TRUE )
|
||||
endif()
|
||||
if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 )
|
||||
set( HAS_CPP17_FLAG TRUE )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" )
|
||||
# as is
|
||||
message( STATUS "Matched: Intel")
|
||||
else()
|
||||
# as is
|
||||
message( STATUS "Matched: nothing")
|
||||
endif()
|
||||
|
||||
# enable MS C++ Core Guidelines checker if MSVC:
|
||||
|
||||
function( enable_msvs_guideline_checker target )
|
||||
if( MSVC )
|
||||
set_target_properties( ${target} PROPERTIES
|
||||
VS_GLOBAL_EnableCppCoreCheck true
|
||||
VS_GLOBAL_CodeAnalysisRuleSet CppCoreCheckRules.ruleset
|
||||
VS_GLOBAL_RunCodeAnalysis true )
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# make target, compile for given standard if specified:
|
||||
|
||||
function( make_target target std )
|
||||
message( STATUS "Make target: '${std}'" )
|
||||
|
||||
add_executable ( ${target} ${SOURCES} )
|
||||
target_include_directories( ${target} SYSTEM PRIVATE lest )
|
||||
target_include_directories( ${target} PRIVATE ${TWEAKD} )
|
||||
target_link_libraries ( ${target} PRIVATE ${PACKAGE} )
|
||||
target_compile_options ( ${target} PRIVATE ${OPTIONS} )
|
||||
target_compile_definitions( ${target} PRIVATE ${DEFINITIONS} )
|
||||
|
||||
if( std )
|
||||
if( MSVC )
|
||||
target_compile_options( ${target} PRIVATE -std:c++${std} )
|
||||
else()
|
||||
# Necessary for clang 3.x:
|
||||
target_compile_options( ${target} PRIVATE -std=c++${std} )
|
||||
# Ok for clang 4 and later:
|
||||
# set( CMAKE_CXX_STANDARD ${std} )
|
||||
# set( CMAKE_CXX_STANDARD_REQUIRED ON )
|
||||
# set( CMAKE_CXX_EXTENSIONS OFF )
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# add generic executable, unless -std flags can be specified:
|
||||
|
||||
if( NOT HAS_STD_FLAGS )
|
||||
make_target( ${PROGRAM}.t "" )
|
||||
else()
|
||||
# # unconditionally add C++98 variant as MSVC has no option for it:
|
||||
# if( HAS_CPP98_FLAG )
|
||||
# make_target( ${PROGRAM}-cpp98.t 98 )
|
||||
# else()
|
||||
# make_target( ${PROGRAM}-cpp98.t "" )
|
||||
# endif()
|
||||
|
||||
# unconditionally add C++11 variant as MSVC has no option for it:
|
||||
if( HAS_CPP11_FLAG )
|
||||
make_target( ${PROGRAM}-cpp11.t 11 )
|
||||
else()
|
||||
make_target( ${PROGRAM}-cpp11.t "" )
|
||||
endif()
|
||||
|
||||
if( HAS_CPP14_FLAG )
|
||||
make_target( ${PROGRAM}-cpp14.t 14 )
|
||||
endif()
|
||||
|
||||
if( HAS_CPP17_FLAG )
|
||||
set( std17 17 )
|
||||
if( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" )
|
||||
set( std17 1z )
|
||||
endif()
|
||||
make_target( ${PROGRAM}-cpp17.t ${std17} )
|
||||
enable_msvs_guideline_checker( ${PROGRAM}-cpp17.t )
|
||||
endif()
|
||||
|
||||
if( HAS_CPPLATEST_FLAG )
|
||||
make_target( ${PROGRAM}-cpplatest.t latest )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# with C++20, honour explicit request for std::expected or nonstd::expected:
|
||||
|
||||
if( HAS_CPP20_FLAG )
|
||||
set( WHICH nsel_EXPECTED_DEFAULT )
|
||||
|
||||
if( EXPECTED_LITE_OPT_SELECT_STD )
|
||||
set( WHICH nsel_EXPECTED_STD )
|
||||
elseif( EXPECTED_LITE_OPT_SELECT_NONSTD )
|
||||
set( WHICH nsel_EXPECTED_NONSTD )
|
||||
endif()
|
||||
|
||||
target_compile_definitions( ${PROGRAM}-cpp17.t PRIVATE nsel_CONFIG_SELECT_EXPECTED=${WHICH} )
|
||||
|
||||
if( HAS_CPPLATEST_FLAG )
|
||||
target_compile_definitions( ${PROGRAM}-cpplatest.t PRIVATE nsel_CONFIG_SELECT_EXPECTED=${WHICH} )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# configure unit tests via CTest:
|
||||
|
||||
enable_testing()
|
||||
|
||||
if( HAS_STD_FLAGS )
|
||||
# # unconditionally add C++98 variant for MSVC:
|
||||
# add_test( NAME test-cpp98 COMMAND ${PROGRAM}-cpp98.t )
|
||||
#
|
||||
|
||||
# unconditionally add C++11 variant for MSVC:
|
||||
add_test( NAME test-cpp11 COMMAND ${PROGRAM}-cpp11.t )
|
||||
|
||||
if( HAS_CPP14_FLAG )
|
||||
add_test( NAME test-cpp14 COMMAND ${PROGRAM}-cpp14.t )
|
||||
endif()
|
||||
if( HAS_CPP17_FLAG )
|
||||
add_test( NAME test-cpp17 COMMAND ${PROGRAM}-cpp17.t )
|
||||
endif()
|
||||
if( HAS_CPPLATEST_FLAG )
|
||||
add_test( NAME test-cpplatest COMMAND ${PROGRAM}-cpplatest.t )
|
||||
endif()
|
||||
else()
|
||||
add_test( NAME test COMMAND ${PROGRAM}.t --pass )
|
||||
add_test( NAME list_version COMMAND ${PROGRAM}.t --version )
|
||||
add_test( NAME list_tags COMMAND ${PROGRAM}.t --list-tags )
|
||||
add_test( NAME list_tests COMMAND ${PROGRAM}.t --list-tests )
|
||||
endif()
|
||||
|
||||
# end of file
|
||||
131
cmake/external/glib/cppgir/expected-lite/test/expected-main.t.cpp
vendored
Normal file
131
cmake/external/glib/cppgir/expected-lite/test/expected-main.t.cpp
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright (c) 2016-2018 Martin Moene
|
||||
//
|
||||
// https://github.com/martinmoene/expected-lite
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "expected-main.t.hpp"
|
||||
|
||||
#define expected_PRESENT( x ) \
|
||||
std::cout << #x << ": " << x << "\n"
|
||||
|
||||
#define expected_ABSENT( x ) \
|
||||
std::cout << #x << ": (undefined)\n"
|
||||
|
||||
// Suppress:
|
||||
// - unused parameter, for cases without assertions such as [.std...]
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#elif defined __GNUC__
|
||||
# pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
|
||||
lest::tests & specification()
|
||||
{
|
||||
static lest::tests tests;
|
||||
return tests;
|
||||
}
|
||||
|
||||
CASE( "expected-lite version" "[.expected][.version]" )
|
||||
{
|
||||
expected_PRESENT( expected_lite_MAJOR );
|
||||
expected_PRESENT( expected_lite_MINOR );
|
||||
expected_PRESENT( expected_lite_PATCH );
|
||||
expected_PRESENT( expected_lite_VERSION );
|
||||
}
|
||||
|
||||
CASE( "any configuration" "[.expected][.config]" )
|
||||
{
|
||||
expected_PRESENT( nsel_HAVE_STD_EXPECTED );
|
||||
expected_PRESENT( nsel_USES_STD_EXPECTED );
|
||||
expected_PRESENT( nsel_EXPECTED_DEFAULT );
|
||||
expected_PRESENT( nsel_EXPECTED_NONSTD );
|
||||
expected_PRESENT( nsel_EXPECTED_STD );
|
||||
expected_PRESENT( nsel_CONFIG_SELECT_EXPECTED );
|
||||
expected_PRESENT( nsel_CONFIG_NO_EXCEPTIONS );
|
||||
expected_PRESENT( nsel_CPLUSPLUS );
|
||||
}
|
||||
|
||||
CASE( "__cplusplus" "[.stdc++]" )
|
||||
{
|
||||
expected_PRESENT( __cplusplus );
|
||||
|
||||
#ifdef _MSVC_LANG
|
||||
expected_PRESENT( _MSVC_LANG );
|
||||
#else
|
||||
expected_ABSENT( _MSVC_LANG );
|
||||
#endif
|
||||
}
|
||||
|
||||
CASE( "Compiler version" "[.compiler]" )
|
||||
{
|
||||
#if nsel_USES_STD_EXPECTED
|
||||
std::cout << "(Compiler version not available: using std::expected)\n";
|
||||
#else
|
||||
expected_PRESENT( nsel_COMPILER_CLANG_VERSION );
|
||||
expected_PRESENT( nsel_COMPILER_GNUC_VERSION );
|
||||
expected_PRESENT( nsel_COMPILER_MSVC_VERSION );
|
||||
#endif
|
||||
}
|
||||
|
||||
CASE( "presence of C++ language features" "[.stdlanguage]" )
|
||||
{
|
||||
#if nsel_USES_STD_EXPECTED
|
||||
std::cout << "(Presence of C++ language features not available: using std::expected)\n";
|
||||
#else
|
||||
std::cout << "[.stdlanguage]: none\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
CASE( "presence of C++ library features" "[.stdlibrary]" )
|
||||
{
|
||||
#if nsel_USES_STD_EXPECTED
|
||||
std::cout << "(Presence of C++ library features not available: using std::expected)\n";
|
||||
#else
|
||||
|
||||
#ifdef __cpp_exceptions
|
||||
expected_PRESENT( __cpp_exceptions );
|
||||
#else
|
||||
expected_ABSENT( __cpp_exceptions );
|
||||
#endif
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
expected_PRESENT( __EXCEPTIONS );
|
||||
#else
|
||||
expected_ABSENT( __EXCEPTIONS );
|
||||
#endif
|
||||
|
||||
#ifdef _HAS_EXCEPTIONS
|
||||
expected_PRESENT( _HAS_EXCEPTIONS );
|
||||
#else
|
||||
expected_ABSENT( _HAS_EXCEPTIONS );
|
||||
#endif
|
||||
|
||||
#ifdef _CPPUNWIND
|
||||
expected_PRESENT( _CPPUNWIND );
|
||||
#else
|
||||
expected_ABSENT( _CPPUNWIND );
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
int main( int argc, char * argv[] )
|
||||
{
|
||||
return lest::run( specification(), argc, argv );
|
||||
}
|
||||
|
||||
#if 0
|
||||
g++ -I../include -o expected-lite.t.exe expected-lite.t.cpp && expected-lite.t.exe --pass
|
||||
g++ -std=c++98 -I../include -o expected-lite.t.exe expected-lite.t.cpp && expected-lite.t.exe --pass
|
||||
g++ -std=c++03 -I../include -o expected-lite.t.exe expected-lite.t.cpp && expected-lite.t.exe --pass
|
||||
g++ -std=c++0x -I../include -o expected-lite.t.exe expected-lite.t.cpp && expected-lite.t.exe --pass
|
||||
g++ -std=c++11 -I../include -o expected-lite.t.exe expected-lite.t.cpp && expected-lite.t.exe --pass
|
||||
g++ -std=c++14 -I../include -o expected-lite.t.exe expected-lite.t.cpp && expected-lite.t.exe --pass
|
||||
g++ -std=c++17 -I../include -o expected-lite.t.exe expected-lite.t.cpp && expected-lite.t.exe --pass
|
||||
|
||||
cl -EHsc -I../include expected-lite.t.cpp && expected-lite.t.exe --pass
|
||||
#endif
|
||||
|
||||
// end of file
|
||||
74
cmake/external/glib/cppgir/expected-lite/test/expected-main.t.hpp
vendored
Normal file
74
cmake/external/glib/cppgir/expected-lite/test/expected-main.t.hpp
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2016-2018 Martin Moene
|
||||
//
|
||||
// https://github.com/martinmoene/expected-lite
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef TEST_EXPECTED_LITE_H_INCLUDED
|
||||
#define TEST_EXPECTED_LITE_H_INCLUDED
|
||||
|
||||
// Limit C++ Core Guidelines checking to expected-lite:
|
||||
|
||||
#include "nonstd/expected.hpp"
|
||||
|
||||
#if nsel_COMPILER_MSVC_VER >= 1910
|
||||
# include <CppCoreCheck/Warnings.h>
|
||||
# pragma warning(disable: ALL_CPPCORECHECK_WARNINGS)
|
||||
#endif
|
||||
|
||||
#include <iosfwd>
|
||||
namespace lest {
|
||||
|
||||
template< typename T, typename E >
|
||||
std::ostream & operator<<( std::ostream & os, nonstd::expected<T,E> const & );
|
||||
|
||||
template< typename E >
|
||||
std::ostream & operator<<( std::ostream & os, nonstd::expected<void,E> const & );
|
||||
} // namespace lest
|
||||
|
||||
// Compiler warning suppression for usage of lest:
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic ignored "-Wstring-conversion"
|
||||
# pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
# pragma clang diagnostic ignored "-Wunused-template"
|
||||
# pragma clang diagnostic ignored "-Wunused-function"
|
||||
# pragma clang diagnostic ignored "-Wunused-member-function"
|
||||
#elif defined __GNUC__
|
||||
# pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
# pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
#include "lest.hpp"
|
||||
|
||||
#define CASE( name ) lest_CASE( specification(), name )
|
||||
|
||||
extern lest::tests & specification();
|
||||
|
||||
namespace lest {
|
||||
|
||||
// use oparator<< instead of to_string() overload;
|
||||
// see http://stackoverflow.com/a/10651752/437272
|
||||
|
||||
template< typename T, typename E >
|
||||
inline std::ostream & operator<<( std::ostream & os, nonstd::expected<T,E> const & v )
|
||||
{
|
||||
using lest::to_string;
|
||||
return os << "[expected:" << (v ? to_string(*v) : "[empty]") << "]";
|
||||
}
|
||||
|
||||
template< typename E >
|
||||
inline std::ostream & operator<<( std::ostream & os, nonstd::expected<void,E> const & v )
|
||||
{
|
||||
using lest::to_string;
|
||||
return os << "[expected<void>:" << (v ? "[non-empty]" : "[empty]") << "]";
|
||||
}
|
||||
|
||||
} // namespace lest
|
||||
|
||||
#endif // TEST_EXPECTED_LITE_H_INCLUDED
|
||||
|
||||
// end of file
|
||||
78
cmake/external/glib/cppgir/expected-lite/test/expected-noexcept.t.cpp
vendored
Normal file
78
cmake/external/glib/cppgir/expected-lite/test/expected-noexcept.t.cpp
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2016-2020 Martin Moene
|
||||
//
|
||||
// https://github.com/martinmoene/expected-lite
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "nonstd/expected.hpp"
|
||||
#include <iostream>
|
||||
|
||||
template< typename T >
|
||||
void use( T const & /*x*/) {}
|
||||
|
||||
#define expected_PRESENT( x ) \
|
||||
std::cout << #x << ": " << x << "\n"
|
||||
|
||||
#define expected_ABSENT( x ) \
|
||||
std::cout << #x << ": (undefined)\n"
|
||||
|
||||
void report()
|
||||
{
|
||||
#ifdef __cpp_exceptions
|
||||
expected_PRESENT( __cpp_exceptions );
|
||||
#else
|
||||
expected_ABSENT( __cpp_exceptions );
|
||||
#endif
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
expected_PRESENT( __EXCEPTIONS );
|
||||
#else
|
||||
expected_ABSENT( __EXCEPTIONS );
|
||||
#endif
|
||||
|
||||
#ifdef _HAS_EXCEPTIONS
|
||||
expected_PRESENT( _HAS_EXCEPTIONS );
|
||||
#else
|
||||
expected_ABSENT( _HAS_EXCEPTIONS );
|
||||
#endif
|
||||
|
||||
#ifdef _CPPUNWIND
|
||||
expected_PRESENT( _CPPUNWIND );
|
||||
#else
|
||||
expected_ABSENT( _CPPUNWIND );
|
||||
#endif
|
||||
|
||||
#ifdef _CPPRTTI
|
||||
expected_PRESENT( _CPPRTTI );
|
||||
#else
|
||||
expected_ABSENT( _CPPRTTI );
|
||||
#endif
|
||||
}
|
||||
|
||||
int violate_access()
|
||||
{
|
||||
nonstd::expected<int, char> eu( nonstd:: make_unexpected('a') );
|
||||
|
||||
return eu.value();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
report();
|
||||
|
||||
#if ! nsel_CONFIG_NO_EXCEPTIONS_SEH
|
||||
return violate_access();
|
||||
#else
|
||||
__try
|
||||
{
|
||||
return violate_access();
|
||||
}
|
||||
__except( EXCEPTION_EXECUTE_HANDLER )
|
||||
{
|
||||
std::cerr << "\n*** Executing SEH __except block ***\n";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// end of file
|
||||
1839
cmake/external/glib/cppgir/expected-lite/test/expected.t.cpp
vendored
Normal file
1839
cmake/external/glib/cppgir/expected-lite/test/expected.t.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1483
cmake/external/glib/cppgir/expected-lite/test/lest/lest.hpp
vendored
Normal file
1483
cmake/external/glib/cppgir/expected-lite/test/lest/lest.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
cmake/external/glib/cppgir/expected-lite/test/nonstd/expected.tweak.hpp
vendored
Normal file
1
cmake/external/glib/cppgir/expected-lite/test/nonstd/expected.tweak.hpp
vendored
Normal file
@@ -0,0 +1 @@
|
||||
#define EXPECTED_TWEAK_VALUE 42
|
||||
3
cmake/external/glib/cppgir/expected-lite/test/odr.cpp
vendored
Normal file
3
cmake/external/glib/cppgir/expected-lite/test/odr.cpp
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "nonstd/expected.hpp"
|
||||
|
||||
MAIN
|
||||
63
cmake/external/glib/cppgir/expected-lite/test/t-noexcept.bat
vendored
Normal file
63
cmake/external/glib/cppgir/expected-lite/test/t-noexcept.bat
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
@echo off & setlocal enableextensions enabledelayedexpansion
|
||||
::
|
||||
:: t.bat - compile & run tests (MSVC).
|
||||
::
|
||||
|
||||
set unit=expected
|
||||
|
||||
:: if no std is given, use compiler default
|
||||
|
||||
set std=%1
|
||||
if not "%std%"=="" set std=-std:%std%
|
||||
|
||||
call :CompilerVersion version
|
||||
echo VC%version%: %args%
|
||||
|
||||
set UCAP=%unit%
|
||||
call :toupper UCAP
|
||||
|
||||
set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_DEFAULT
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_NONSTD
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_STD
|
||||
|
||||
set unit_config=
|
||||
|
||||
set msvc_defines=^
|
||||
-Dlest_FEATURE_AUTO_REGISTER=1 ^
|
||||
-D_CRT_SECURE_NO_WARNINGS ^
|
||||
-D_SCL_SECURE_NO_WARNINGS ^
|
||||
-D_HAS_EXCEPTIONS=0 ^
|
||||
-Dnsel_CONFIG_NO_SEH=0
|
||||
|
||||
:: -Dnsel_CONFIG_NO_EXCEPTIONS=1
|
||||
|
||||
set CppCoreCheckInclude=%VCINSTALLDIR%\Auxiliary\VS\include
|
||||
|
||||
:: -EHsc
|
||||
::cl -kernel -GR- -W3 %std% %unit_select% %unit_config% %msvc_defines% -I"%CppCoreCheckInclude%" -I../include -I. %unit%-noexcept.t.cpp && %unit%-noexcept.t.exe
|
||||
cl -EHs -GR- -W3 %std% %unit_select% %unit_config% %msvc_defines% -I"%CppCoreCheckInclude%" -Ilest -I../include -I. %unit%-noexcept.t.cpp && %unit%-noexcept.t.exe
|
||||
endlocal & goto :EOF
|
||||
|
||||
:: subroutines:
|
||||
|
||||
:CompilerVersion version
|
||||
@echo off & setlocal enableextensions
|
||||
set tmpprogram=_getcompilerversion.tmp
|
||||
set tmpsource=%tmpprogram%.c
|
||||
|
||||
echo #include ^<stdio.h^> >%tmpsource%
|
||||
echo int main(){printf("%%d\n",_MSC_VER);} >>%tmpsource%
|
||||
|
||||
cl /nologo %tmpsource% >nul
|
||||
for /f %%x in ('%tmpprogram%') do set version=%%x
|
||||
del %tmpprogram%.* >nul
|
||||
set offset=0
|
||||
if %version% LSS 1900 set /a offset=1
|
||||
set /a version="version / 10 - 10 * ( 5 + offset )"
|
||||
endlocal & set %1=%version%& goto :EOF
|
||||
|
||||
:: toupper; makes use of the fact that string
|
||||
:: replacement (via SET) is not case sensitive
|
||||
:toupper
|
||||
for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L!
|
||||
goto :EOF
|
||||
3
cmake/external/glib/cppgir/expected-lite/test/t-odr.bat
vendored
Normal file
3
cmake/external/glib/cppgir/expected-lite/test/t-odr.bat
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
cl -EHsc -DMAIN="" -Dnsel_P0323R=-2 -I../include -I. -Foodr1.obj -c odr.cpp
|
||||
cl -EHsc -DMAIN="int main(){}" -Dnsel_P0323R=-2 -I../include -I. -Foodr2.obj -c odr.cpp
|
||||
cl odr1.obj odr2.obj
|
||||
57
cmake/external/glib/cppgir/expected-lite/test/t.bat
vendored
Normal file
57
cmake/external/glib/cppgir/expected-lite/test/t.bat
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
@echo off & setlocal enableextensions enabledelayedexpansion
|
||||
::
|
||||
:: t.bat - compile & run tests (MSVC).
|
||||
::
|
||||
|
||||
set unit=expected
|
||||
|
||||
:: if no std is given, use compiler default
|
||||
|
||||
set std=%1
|
||||
if not "%std%"=="" set std=-std:%std%
|
||||
|
||||
call :CompilerVersion version
|
||||
echo VC%version%: %args%
|
||||
|
||||
set UCAP=%unit%
|
||||
call :toupper UCAP
|
||||
|
||||
set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_DEFAULT
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_NONSTD
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_STD
|
||||
|
||||
set unit_config=
|
||||
|
||||
set msvc_defines=^
|
||||
-Dlest_FEATURE_AUTO_REGISTER=1 ^
|
||||
-D_CRT_SECURE_NO_WARNINGS ^
|
||||
-D_SCL_SECURE_NO_WARNINGS
|
||||
|
||||
set CppCoreCheckInclude=%VCINSTALLDIR%\Auxiliary\VS\include
|
||||
|
||||
cl -nologo -W3 -EHsc %std% %unit_select% %unit_config% %msvc_defines% -I"%CppCoreCheckInclude%" -Ilest -I../include -I. %unit%-main.t.cpp %unit%.t.cpp && %unit%-main.t.exe
|
||||
endlocal & goto :EOF
|
||||
|
||||
:: subroutines:
|
||||
|
||||
:CompilerVersion version
|
||||
@echo off & setlocal enableextensions
|
||||
set tmpprogram=_getcompilerversion.tmp
|
||||
set tmpsource=%tmpprogram%.c
|
||||
|
||||
echo #include ^<stdio.h^> >%tmpsource%
|
||||
echo int main(){printf("%%d\n",_MSC_VER);} >>%tmpsource%
|
||||
|
||||
cl /nologo %tmpsource% >nul
|
||||
for /f %%x in ('%tmpprogram%') do set version=%%x
|
||||
del %tmpprogram%.* >nul
|
||||
set offset=0
|
||||
if %version% LSS 1900 set /a offset=1
|
||||
set /a version="version / 10 - 10 * ( 5 + offset )"
|
||||
endlocal & set %1=%version%& goto :EOF
|
||||
|
||||
:: toupper; makes use of the fact that string
|
||||
:: replacement (via SET) is not case sensitive
|
||||
:toupper
|
||||
for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L!
|
||||
goto :EOF
|
||||
60
cmake/external/glib/cppgir/expected-lite/test/tc-cl.bat
vendored
Normal file
60
cmake/external/glib/cppgir/expected-lite/test/tc-cl.bat
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
@echo off & setlocal enableextensions enabledelayedexpansion
|
||||
::
|
||||
:: tc-cl.bat - compile & run tests (clang-cl).
|
||||
::
|
||||
|
||||
set unit=expected
|
||||
set unit_file=%unit%
|
||||
|
||||
:: if no std is given, use c++14
|
||||
|
||||
set std=c++14
|
||||
if NOT "%1" == "" set std=%1 & shift
|
||||
|
||||
set UCAP=%unit%
|
||||
call :toupper UCAP
|
||||
|
||||
set unit_select=%unit%_%UCAP%_NONSTD
|
||||
::set unit_select=%unit%_CONFIG_SELECT_%UCAP%_NONSTD
|
||||
if NOT "%1" == "" set unit_select=%1 & shift
|
||||
|
||||
set args=%1 %2 %3 %4 %5 %6 %7 %8 %9
|
||||
|
||||
set clang=clang-cl
|
||||
|
||||
call :CompilerVersion version
|
||||
echo %clang% %version%: %std% %unit_select% %args%
|
||||
|
||||
set unit_config=^
|
||||
-Dlest_FEATURE_AUTO_REGISTER=1 ^
|
||||
-D%unit%_%UCAP%_HEADER=\"nonstd/%unit%.hpp\" ^
|
||||
-D%unit%_TEST_NODISCARD=0 ^
|
||||
-D%unit%_CONFIG_SELECT_%UCAP%=%unit_select%
|
||||
|
||||
rem -flto / -fwhole-program
|
||||
set optflags=-O2
|
||||
set warnflags=-Wall -Wextra -Wpedantic -Weverything -Wshadow -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-missing-noreturn -Wno-documentation-unknown-command -Wno-documentation-deprecated-sync -Wno-documentation -Wno-weak-vtables -Wno-missing-prototypes -Wno-missing-variable-declarations -Wno-exit-time-destructors -Wno-global-constructors -Wno-sign-conversion -Wno-sign-compare -Wno-implicit-int-conversion -Wno-deprecated-declarations -Wno-date-time
|
||||
|
||||
"%clang%" -EHsc -std:%std% %optflags% %warnflags% %unit_config% -fms-compatibility-version=19.00 /imsvc lest -I../include -Ics_string -I. -o %unit_file%-main.t.exe %unit_file%-main.t.cpp %unit_file%.t.cpp && %unit_file%-main.t.exe
|
||||
endlocal & goto :EOF
|
||||
|
||||
:: subroutines:
|
||||
|
||||
:CompilerVersion version
|
||||
echo off & setlocal enableextensions
|
||||
set tmpprogram=_getcompilerversion.tmp
|
||||
set tmpsource=%tmpprogram%.c
|
||||
|
||||
echo #include ^<stdio.h^> > %tmpsource%
|
||||
echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource%
|
||||
|
||||
"%clang%" -m32 -o %tmpprogram% %tmpsource% >nul
|
||||
for /f %%x in ('%tmpprogram%') do set version=%%x
|
||||
del %tmpprogram%.* >nul
|
||||
endlocal & set %1=%version%& goto :EOF
|
||||
|
||||
:: toupper; makes use of the fact that string
|
||||
:: replacement (via SET) is not case sensitive
|
||||
:toupper
|
||||
for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L!
|
||||
goto :EOF
|
||||
53
cmake/external/glib/cppgir/expected-lite/test/tc-noexcept.bat
vendored
Normal file
53
cmake/external/glib/cppgir/expected-lite/test/tc-noexcept.bat
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
@echo off & setlocal enableextensions enabledelayedexpansion
|
||||
::
|
||||
:: tc.bat - compile & run tests (clang).
|
||||
::
|
||||
|
||||
set unit=expected
|
||||
|
||||
:: if no std is given, use c++14
|
||||
|
||||
set std=%1
|
||||
if "%std%"=="" set std=c++14
|
||||
|
||||
set clang=clang
|
||||
|
||||
call :CompilerVersion version
|
||||
echo %clang% %version%: %std%
|
||||
|
||||
set UCAP=%unit%
|
||||
call :toupper UCAP
|
||||
|
||||
set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_DEFAULT
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_NONSTD
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_STD
|
||||
|
||||
set unit_config=
|
||||
|
||||
rem -flto / -fwhole-program
|
||||
set optflags=-O2 -fno-exceptions
|
||||
set warnflags=-Wall -Wextra -Wpedantic -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-missing-noreturn -Wno-documentation-unknown-command -Wno-documentation-deprecated-sync -Wno-documentation -Wno-weak-vtables -Wno-missing-prototypes -Wno-missing-variable-declarations -Wno-exit-time-destructors -Wno-global-constructors
|
||||
|
||||
"%clang%" -m32 -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -Dlest_FEATURE_AUTO_REGISTER=1 -fms-compatibility-version=19.00 -isystem "%VCInstallDir%include" -isystem "%WindowsSdkDir_71A%include" -I../include -I. -o %unit%-main.t.exe %unit%-noexcept.t.cpp && %unit%-noexcept.t.exe
|
||||
endlocal & goto :EOF
|
||||
|
||||
:: subroutines:
|
||||
|
||||
:CompilerVersion version
|
||||
echo off & setlocal enableextensions
|
||||
set tmpprogram=_getcompilerversion.tmp
|
||||
set tmpsource=%tmpprogram%.c
|
||||
|
||||
echo #include ^<stdio.h^> > %tmpsource%
|
||||
echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource%
|
||||
|
||||
"%clang%" -m32 -o %tmpprogram% %tmpsource% >nul
|
||||
for /f %%x in ('%tmpprogram%') do set version=%%x
|
||||
del %tmpprogram%.* >nul
|
||||
endlocal & set %1=%version%& goto :EOF
|
||||
|
||||
:: toupper; makes use of the fact that string
|
||||
:: replacement (via SET) is not case sensitive
|
||||
:toupper
|
||||
for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L!
|
||||
goto :EOF
|
||||
53
cmake/external/glib/cppgir/expected-lite/test/tc.bat
vendored
Normal file
53
cmake/external/glib/cppgir/expected-lite/test/tc.bat
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
@echo off & setlocal enableextensions enabledelayedexpansion
|
||||
::
|
||||
:: tc.bat - compile & run tests (clang).
|
||||
::
|
||||
|
||||
set unit=expected
|
||||
|
||||
:: if no std is given, use c++14
|
||||
|
||||
set std=%1
|
||||
if "%std%"=="" set std=c++14
|
||||
|
||||
set clang=clang
|
||||
|
||||
call :CompilerVersion version
|
||||
echo %clang% %version%: %std%
|
||||
|
||||
set UCAP=%unit%
|
||||
call :toupper UCAP
|
||||
|
||||
set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_DEFAULT
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_NONSTD
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_STD
|
||||
|
||||
set unit_config=
|
||||
|
||||
rem -flto / -fwhole-program
|
||||
set optflags=-O2
|
||||
set warnflags=-Wall -Wextra -Wpedantic -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-missing-noreturn -Wno-documentation-unknown-command -Wno-documentation-deprecated-sync -Wno-documentation -Wno-weak-vtables -Wno-missing-prototypes -Wno-missing-variable-declarations -Wno-exit-time-destructors -Wno-global-constructors
|
||||
|
||||
"%clang%" -m32 -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -Dlest_FEATURE_AUTO_REGISTER=1 -fms-compatibility-version=19.00 -isystem "%VCInstallDir%include" -isystem "%WindowsSdkDir_71A%include" -isystem lest -I../include -I. -o %unit%-main.t.exe %unit%-main.t.cpp %unit%.t.cpp && %unit%-main.t.exe
|
||||
endlocal & goto :EOF
|
||||
|
||||
:: subroutines:
|
||||
|
||||
:CompilerVersion version
|
||||
echo off & setlocal enableextensions
|
||||
set tmpprogram=_getcompilerversion.tmp
|
||||
set tmpsource=%tmpprogram%.c
|
||||
|
||||
echo #include ^<stdio.h^> > %tmpsource%
|
||||
echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource%
|
||||
|
||||
"%clang%" -m32 -o %tmpprogram% %tmpsource% >nul
|
||||
for /f %%x in ('%tmpprogram%') do set version=%%x
|
||||
del %tmpprogram%.* >nul
|
||||
endlocal & set %1=%version%& goto :EOF
|
||||
|
||||
:: toupper; makes use of the fact that string
|
||||
:: replacement (via SET) is not case sensitive
|
||||
:toupper
|
||||
for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L!
|
||||
goto :EOF
|
||||
3
cmake/external/glib/cppgir/expected-lite/test/tg-all.bat
vendored
Normal file
3
cmake/external/glib/cppgir/expected-lite/test/tg-all.bat
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
@for %%s in ( c++98 c++03 c++11 c++14 c++17 ) do (
|
||||
call tg.bat %%s
|
||||
)
|
||||
55
cmake/external/glib/cppgir/expected-lite/test/tg-noexcept.bat
vendored
Normal file
55
cmake/external/glib/cppgir/expected-lite/test/tg-noexcept.bat
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
@echo off & setlocal enableextensions enabledelayedexpansion
|
||||
::
|
||||
:: tg.bat - compile & run tests (GNUC).
|
||||
::
|
||||
|
||||
set unit=expected
|
||||
|
||||
:: if no std is given, use c++11
|
||||
|
||||
set std=%1
|
||||
set args=%2 %3 %4 %5 %6 %7 %8 %9
|
||||
if "%1" == "" set std=c++11
|
||||
|
||||
set gpp=g++
|
||||
|
||||
call :CompilerVersion version
|
||||
echo %gpp% %version%: %std% %args%
|
||||
|
||||
set UCAP=%unit%
|
||||
call :toupper UCAP
|
||||
|
||||
set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_DEFAULT
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_NONSTD
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_STD
|
||||
|
||||
set unit_config=-Dnsel_CONFIG_NO_EXCEPTIONS=1
|
||||
|
||||
rem -flto / -fwhole-program
|
||||
set optflags=-O2 -fno-exceptions
|
||||
set warnflags=-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wno-padded -Wno-missing-noreturn
|
||||
|
||||
%gpp% -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -o %unit%-main.t.exe -Dlest_FEATURE_AUTO_REGISTER=1 -I../include -I. %unit%-noexcept.t.cpp && %unit%-main.t.exe
|
||||
|
||||
endlocal & goto :EOF
|
||||
|
||||
:: subroutines:
|
||||
|
||||
:CompilerVersion version
|
||||
echo off & setlocal enableextensions
|
||||
set tmpprogram=_getcompilerversion.tmp
|
||||
set tmpsource=%tmpprogram%.c
|
||||
|
||||
echo #include ^<stdio.h^> > %tmpsource%
|
||||
echo int main(){printf("%%d.%%d.%%d\n",__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__);} >> %tmpsource%
|
||||
|
||||
%gpp% -o %tmpprogram% %tmpsource% >nul
|
||||
for /f %%x in ('%tmpprogram%') do set version=%%x
|
||||
del %tmpprogram%.* >nul
|
||||
endlocal & set %1=%version%& goto :EOF
|
||||
|
||||
:: toupper; makes use of the fact that string
|
||||
:: replacement (via SET) is not case sensitive
|
||||
:toupper
|
||||
for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L!
|
||||
goto :EOF
|
||||
55
cmake/external/glib/cppgir/expected-lite/test/tg.bat
vendored
Normal file
55
cmake/external/glib/cppgir/expected-lite/test/tg.bat
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
@echo off & setlocal enableextensions enabledelayedexpansion
|
||||
::
|
||||
:: tg.bat - compile & run tests (GNUC).
|
||||
::
|
||||
|
||||
set unit=expected
|
||||
|
||||
:: if no std is given, use c++11
|
||||
|
||||
set std=%1
|
||||
set args=%2 %3 %4 %5 %6 %7 %8 %9
|
||||
if "%1" == "" set std=c++11
|
||||
|
||||
set gpp=g++
|
||||
|
||||
call :CompilerVersion version
|
||||
echo %gpp% %version%: %std% %args%
|
||||
|
||||
set UCAP=%unit%
|
||||
call :toupper UCAP
|
||||
|
||||
set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_DEFAULT
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_NONSTD
|
||||
::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_STD
|
||||
|
||||
set unit_config=
|
||||
|
||||
rem -flto / -fwhole-program
|
||||
set optflags=-O2
|
||||
set warnflags=-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wno-padded -Wno-missing-noreturn
|
||||
|
||||
%gpp% -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -o %unit%-main.t.exe -Dlest_FEATURE_AUTO_REGISTER=1 -isystem lest -I../include -I. %unit%-main.t.cpp %unit%.t.cpp && %unit%-main.t.exe
|
||||
|
||||
endlocal & goto :EOF
|
||||
|
||||
:: subroutines:
|
||||
|
||||
:CompilerVersion version
|
||||
echo off & setlocal enableextensions
|
||||
set tmpprogram=_getcompilerversion.tmp
|
||||
set tmpsource=%tmpprogram%.c
|
||||
|
||||
echo #include ^<stdio.h^> > %tmpsource%
|
||||
echo int main(){printf("%%d.%%d.%%d\n",__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__);} >> %tmpsource%
|
||||
|
||||
%gpp% -o %tmpprogram% %tmpsource% >nul
|
||||
for /f %%x in ('%tmpprogram%') do set version=%%x
|
||||
del %tmpprogram%.* >nul
|
||||
endlocal & set %1=%version%& goto :EOF
|
||||
|
||||
:: toupper; makes use of the fact that string
|
||||
:: replacement (via SET) is not case sensitive
|
||||
:toupper
|
||||
for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L!
|
||||
goto :EOF
|
||||
12
cmake/external/glib/cppgir/format.sh
vendored
Executable file
12
cmake/external/glib/cppgir/format.sh
vendored
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
cd $1
|
||||
echo -n "Running dos2unix "
|
||||
git ls-files | grep '\.cpp$\|\.h$\|\.hpp$' | \
|
||||
xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'"
|
||||
echo
|
||||
echo -n "Running clang-format "
|
||||
git ls-files | grep '\.cpp$\|\.h$\|\.hpp$' | \
|
||||
xargs -I {} sh -c "clang-format -i {}; echo -n '.'"
|
||||
echo
|
||||
|
||||
|
||||
503
cmake/external/glib/cppgir/gi/base.hpp
vendored
Normal file
503
cmake/external/glib/cppgir/gi/base.hpp
vendored
Normal file
@@ -0,0 +1,503 @@
|
||||
#ifndef GI_BASE_HPP
|
||||
#define GI_BASE_HPP
|
||||
|
||||
#include "gi_inc.hpp"
|
||||
|
||||
#include "boxed.hpp"
|
||||
#include "objectbase.hpp"
|
||||
|
||||
// un-inline some glib parts
|
||||
// (otherwise they have internal linkage and not usable in non-TU-local context)
|
||||
#ifdef GI_MODULE_IN_INTERFACE
|
||||
#ifdef g_strdup
|
||||
#undef g_strdup
|
||||
#endif
|
||||
#endif
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
inline std::string
|
||||
exception_desc(const std::exception &e)
|
||||
{
|
||||
auto desc = e.what();
|
||||
return desc ? desc : typeid(e).name();
|
||||
}
|
||||
|
||||
inline std::string
|
||||
exception_desc(...)
|
||||
{
|
||||
return "[unknown]";
|
||||
}
|
||||
|
||||
template<typename E>
|
||||
[[noreturn]] inline void
|
||||
try_throw(E &&e)
|
||||
{
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
throw std::forward<E>(e);
|
||||
#else
|
||||
g_critical("no throw exception; %s", exception_desc(e).c_str());
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
// constructor does not appreciate NULL, so wrap that here
|
||||
// map NULL to empty string; not quite the same, but it will do
|
||||
inline std::string
|
||||
make_string(const char *s)
|
||||
{
|
||||
return std::string(s ? s : "");
|
||||
}
|
||||
|
||||
// helper string subtype
|
||||
// used to overload unwrap of optional string argument
|
||||
// (transfrom empty string to null)
|
||||
// NOTE std::optional requires C++17
|
||||
class optional_string : public std::string
|
||||
{};
|
||||
|
||||
class noncopyable
|
||||
{
|
||||
public:
|
||||
noncopyable() {}
|
||||
noncopyable(const noncopyable &) = delete;
|
||||
noncopyable &operator=(const noncopyable &) = delete;
|
||||
|
||||
noncopyable(noncopyable &&) = default;
|
||||
noncopyable &operator=(noncopyable &&) = default;
|
||||
};
|
||||
|
||||
class scope_guard : public noncopyable
|
||||
{
|
||||
private:
|
||||
std::function<void()> cleanup_;
|
||||
|
||||
public:
|
||||
scope_guard(std::function<void()> &&cleanup) : cleanup_(std::move(cleanup)) {}
|
||||
|
||||
~scope_guard() noexcept(false)
|
||||
{
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
#if __cplusplus >= 201703L
|
||||
auto pending = std::uncaught_exceptions();
|
||||
#else
|
||||
auto pending = std::uncaught_exception();
|
||||
#endif
|
||||
try {
|
||||
#endif
|
||||
cleanup_();
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
} catch (...) {
|
||||
if (!pending)
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
// as in
|
||||
// http://ericniebler.com/2013/08/07/universal-references-and-the-copy-constructo/
|
||||
template<typename A, typename B>
|
||||
using disable_if_same_or_derived = typename std::enable_if<
|
||||
!std::is_base_of<A, typename std::remove_reference<B>::type>::value>::type;
|
||||
} // namespace detail
|
||||
|
||||
namespace repository
|
||||
{
|
||||
// class types declare c type within class
|
||||
// others can do so using this (e.g. enum)
|
||||
template<typename CppType>
|
||||
struct declare_ctype_of
|
||||
{};
|
||||
|
||||
// and for all cases the reverse cpp type
|
||||
template<typename CType>
|
||||
struct declare_cpptype_of
|
||||
{};
|
||||
|
||||
// generate code must specialize appropriately
|
||||
template<typename T>
|
||||
struct is_enumeration : public std::false_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct is_bitfield : public std::false_type
|
||||
{};
|
||||
|
||||
} // namespace repository
|
||||
|
||||
struct transfer_full_t;
|
||||
struct transfer_none_t;
|
||||
|
||||
namespace traits
|
||||
{
|
||||
template<typename T, typename U = void>
|
||||
struct if_valid_type
|
||||
{
|
||||
typedef U type;
|
||||
};
|
||||
|
||||
template<typename, typename = void>
|
||||
struct is_type_complete : public std::false_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct is_type_complete<T, typename if_valid_type<decltype(sizeof(
|
||||
typename std::decay<T>::type))>::type>
|
||||
: public std::true_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
using is_decayed = std::is_same<typename std::decay<T>::type, T>;
|
||||
|
||||
template<typename T>
|
||||
using is_cboxed =
|
||||
typename std::conditional<std::is_base_of<detail::CBoxed, T>::value,
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
template<typename T>
|
||||
using is_gboxed =
|
||||
typename std::conditional<std::is_base_of<detail::GBoxed, T>::value,
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
template<typename T>
|
||||
using is_boxed =
|
||||
typename std::conditional<std::is_base_of<detail::Boxed, T>::value,
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
// avoid derived cases
|
||||
template<typename T>
|
||||
using is_object =
|
||||
typename std::conditional<std::is_base_of<detail::ObjectBase, T>::value &&
|
||||
sizeof(T) == sizeof(gpointer),
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
template<typename T>
|
||||
using is_wrapper =
|
||||
typename std::conditional<std::is_base_of<detail::wrapper_tag, T>::value &&
|
||||
sizeof(T) == sizeof(gpointer),
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
// bring in to this namespace
|
||||
using repository::is_bitfield;
|
||||
using repository::is_enumeration;
|
||||
|
||||
// aka passthrough
|
||||
template<typename T>
|
||||
using is_basic =
|
||||
typename std::conditional<std::is_same<T, gpointer>::value ||
|
||||
std::is_same<T, gconstpointer>::value ||
|
||||
std::is_arithmetic<T>::value,
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
// almost passthrough (on lower level at least)
|
||||
template<typename T>
|
||||
using is_plain = typename std::conditional<traits::is_basic<T>::value ||
|
||||
std::is_enum<T>::value,
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
template<typename T, typename E = void>
|
||||
struct is_reftype : public std::false_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct is_reftype<T,
|
||||
typename if_valid_type<typename std::decay<T>::type::BoxType>::type>
|
||||
: public std::true_type
|
||||
{};
|
||||
|
||||
template<typename T, typename Enable = void>
|
||||
struct has_ctype_member : public std::false_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct has_ctype_member<T,
|
||||
typename if_valid_type<typename T::BaseObjectType>::type>
|
||||
: public std::true_type
|
||||
{};
|
||||
|
||||
// return corresponding c type (if any)
|
||||
// (string and basic type not considered)
|
||||
// preserve const
|
||||
template<typename T, typename Enable = void>
|
||||
struct ctype
|
||||
{};
|
||||
|
||||
// class case
|
||||
template<typename T>
|
||||
struct ctype<T,
|
||||
typename if_valid_type<typename std::decay<T>::type::BaseObjectType>::type>
|
||||
{
|
||||
typedef typename std::remove_reference<T>::type CppType;
|
||||
// make sure; avoid subclassed cases
|
||||
static_assert(is_wrapper<CppType>::value || is_boxed<CppType>::value,
|
||||
"must be object or boxed wrapper");
|
||||
typedef typename CppType::BaseObjectType CType;
|
||||
typedef typename std::conditional<std::is_const<CppType>::value, const CType,
|
||||
CType>::type *type;
|
||||
};
|
||||
|
||||
// remaining cases
|
||||
template<typename T>
|
||||
struct ctype<T, typename if_valid_type<
|
||||
typename repository::declare_ctype_of<T>::type>::type>
|
||||
{
|
||||
typedef typename repository::declare_ctype_of<T>::type CType;
|
||||
typedef typename std::conditional<std::is_const<T>::value, const CType,
|
||||
CType>::type type;
|
||||
};
|
||||
|
||||
// basic cases passthrough
|
||||
template<typename T>
|
||||
struct ctype<T,
|
||||
typename std::enable_if<(std::is_fundamental<T>::value &&
|
||||
!std::is_same<T, bool>::value) ||
|
||||
std::is_same<T, gpointer>::value ||
|
||||
std::is_same<T, gconstpointer>::value>::type>
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
// ... exception though for bool
|
||||
template<>
|
||||
struct ctype<bool, void>
|
||||
{
|
||||
typedef gboolean type;
|
||||
};
|
||||
|
||||
// as used in callback signatures
|
||||
// or in list (un)wrapping
|
||||
template<>
|
||||
struct ctype<const std::string &, void>
|
||||
{
|
||||
typedef const char *type;
|
||||
};
|
||||
template<>
|
||||
struct ctype<std::string, void>
|
||||
{
|
||||
typedef char *type;
|
||||
};
|
||||
|
||||
template<typename T1, typename T2>
|
||||
struct ctype<std::pair<T1, T2>>
|
||||
{
|
||||
typedef std::pair<typename ctype<T1>::type, typename ctype<T2>::type> type;
|
||||
};
|
||||
|
||||
// conversely
|
||||
// return corresponding cpp type (if known)
|
||||
// (string and basic type not considered)
|
||||
// preserve const
|
||||
template<typename T, typename Transfer = transfer_full_t,
|
||||
typename Enable = void>
|
||||
struct cpptype
|
||||
{};
|
||||
|
||||
// generic
|
||||
template<typename T>
|
||||
struct cpptype<T *, transfer_full_t,
|
||||
typename if_valid_type<typename repository::declare_cpptype_of<
|
||||
typename std::remove_const<T>::type>::type>::type>
|
||||
{
|
||||
typedef typename repository::declare_cpptype_of<
|
||||
typename std::remove_const<T>::type>::type CppType;
|
||||
typedef typename std::conditional<std::is_const<T>::value, const CppType,
|
||||
CppType>::type type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct cpptype<T, transfer_full_t,
|
||||
typename if_valid_type<typename repository::declare_cpptype_of<
|
||||
typename std::remove_const<T>::type>::type>::type>
|
||||
{
|
||||
typedef typename repository::declare_cpptype_of<
|
||||
typename std::remove_const<T>::type>::type CppType;
|
||||
typedef typename std::conditional<std::is_const<T>::value, const CppType,
|
||||
CppType>::type type;
|
||||
};
|
||||
|
||||
// basic cases passthrough
|
||||
template<typename T>
|
||||
struct cpptype<T, transfer_full_t,
|
||||
typename std::enable_if<std::is_fundamental<T>::value ||
|
||||
std::is_same<T, gpointer>::value ||
|
||||
std::is_same<T, gconstpointer>::value>::type>
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
#if 0
|
||||
template<>
|
||||
struct cpptype<char *, transfer_full_t>
|
||||
{
|
||||
using type = std::string;
|
||||
};
|
||||
#endif
|
||||
|
||||
// handle none transfer case
|
||||
template<typename T>
|
||||
struct cpptype<T, transfer_none_t>
|
||||
{
|
||||
using CppType = typename cpptype<T, transfer_full_t>::type;
|
||||
template<typename TT, typename Enable = void>
|
||||
struct map_type
|
||||
{
|
||||
using type = TT;
|
||||
};
|
||||
template<typename TT>
|
||||
struct map_type<TT, typename if_valid_type<typename TT::ReferenceType>::type>
|
||||
{
|
||||
using type = typename TT::ReferenceType;
|
||||
};
|
||||
using type = typename map_type<CppType>::type;
|
||||
};
|
||||
|
||||
// map owning box type to corresponding reference box type
|
||||
template<typename T>
|
||||
struct reftype
|
||||
{
|
||||
typedef typename T::ReferenceType type;
|
||||
};
|
||||
|
||||
} // namespace traits
|
||||
|
||||
// specify transfer type when (un)wrapping
|
||||
// this approach is safer than some booleans and allows overload combinations
|
||||
struct transfer_t
|
||||
{
|
||||
const int value;
|
||||
constexpr explicit transfer_t(int v = 0) : value(v) {}
|
||||
};
|
||||
struct transfer_none_t : public transfer_t
|
||||
{
|
||||
constexpr transfer_none_t() : transfer_t(0) {}
|
||||
};
|
||||
struct transfer_full_t : public transfer_t
|
||||
{
|
||||
constexpr transfer_full_t() : transfer_t(1) {}
|
||||
};
|
||||
struct transfer_container_t : public transfer_t
|
||||
{
|
||||
constexpr transfer_container_t() : transfer_t(2) {}
|
||||
};
|
||||
|
||||
GI_MODULE_INLINE const constexpr transfer_t transfer_dummy = transfer_t();
|
||||
GI_MODULE_INLINE const constexpr transfer_none_t transfer_none =
|
||||
transfer_none_t();
|
||||
GI_MODULE_INLINE const constexpr transfer_full_t transfer_full =
|
||||
transfer_full_t();
|
||||
GI_MODULE_INLINE const constexpr transfer_container_t transfer_container =
|
||||
transfer_container_t();
|
||||
|
||||
template<typename Transfer>
|
||||
struct element_transfer
|
||||
{};
|
||||
template<>
|
||||
struct element_transfer<transfer_none_t>
|
||||
{
|
||||
typedef transfer_none_t type;
|
||||
};
|
||||
template<>
|
||||
struct element_transfer<transfer_full_t>
|
||||
{
|
||||
typedef transfer_full_t type;
|
||||
};
|
||||
template<>
|
||||
struct element_transfer<transfer_container_t>
|
||||
{
|
||||
typedef transfer_none_t type;
|
||||
};
|
||||
|
||||
// unwrapping a callback
|
||||
// specify call scope type
|
||||
struct scope_t
|
||||
{
|
||||
const int value;
|
||||
constexpr explicit scope_t(int v = 0) : value(v) {}
|
||||
};
|
||||
struct scope_call_t : public scope_t
|
||||
{
|
||||
constexpr scope_call_t() : scope_t(0) {}
|
||||
};
|
||||
struct scope_async_t : public scope_t
|
||||
{
|
||||
constexpr scope_async_t() : scope_t(1) {}
|
||||
};
|
||||
struct scope_notified_t : public scope_t
|
||||
{
|
||||
constexpr scope_notified_t() : scope_t(2) {}
|
||||
};
|
||||
|
||||
GI_MODULE_INLINE const constexpr scope_t scope_dummy = scope_t();
|
||||
GI_MODULE_INLINE const constexpr scope_call_t scope_call = scope_call_t();
|
||||
GI_MODULE_INLINE const constexpr scope_async_t scope_async = scope_async_t();
|
||||
GI_MODULE_INLINE const constexpr scope_notified_t scope_notified =
|
||||
scope_notified_t();
|
||||
|
||||
// (dummy) helper tag to aid in overload resolution
|
||||
template<typename Interface>
|
||||
struct interface_tag
|
||||
{
|
||||
typedef Interface type;
|
||||
};
|
||||
|
||||
// CallArgs minimal helper type
|
||||
// only provide what is needed
|
||||
// not intended for general use, even though not in internal namespace
|
||||
template<typename T>
|
||||
class required
|
||||
{
|
||||
T data_;
|
||||
|
||||
public:
|
||||
constexpr required(T v) : data_(std::move(v)) {}
|
||||
constexpr operator T &() &noexcept { return data_; }
|
||||
constexpr operator T &&() &&noexcept { return std::move(data_); }
|
||||
constexpr T &value() &noexcept { return data_; }
|
||||
constexpr T &&value() &&noexcept { return std::move(data_); }
|
||||
};
|
||||
|
||||
// helper tag type to aid in overload resolution of CallArgs
|
||||
// this is used as the first argument of a (non-plain) signature variant
|
||||
// (since it may otherwise have only 1 struct argument, like the plain variant)
|
||||
template<typename... CA_TAG>
|
||||
using ca = std::tuple<CA_TAG...>;
|
||||
|
||||
struct ca_tag
|
||||
{};
|
||||
struct ca_in_tag : public ca_tag
|
||||
{};
|
||||
struct ca_bc_tag : public ca_tag
|
||||
{};
|
||||
// convenience abbreviation
|
||||
using ca_in = ca<ca_in_tag>;
|
||||
|
||||
#if GI_DL
|
||||
namespace detail
|
||||
{
|
||||
// dynamic load of symbol
|
||||
inline void *
|
||||
load_symbol(const std::vector<const char *> libs, const char *symbol)
|
||||
{
|
||||
void *s = nullptr;
|
||||
for (const auto &l : libs) {
|
||||
auto h = dlopen(l, RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE);
|
||||
if (h) {
|
||||
s = dlsym(h, symbol);
|
||||
dlclose(h);
|
||||
if (s)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
#endif // GI_DL
|
||||
|
||||
} // namespace gi
|
||||
|
||||
#endif // GI_BASE_HPP
|
||||
349
cmake/external/glib/cppgir/gi/boxed.hpp
vendored
Normal file
349
cmake/external/glib/cppgir/gi/boxed.hpp
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
#ifndef GI_BOXED_HPP
|
||||
#define GI_BOXED_HPP
|
||||
|
||||
#include "gi_inc.hpp"
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
namespace repository
|
||||
{
|
||||
// specialize to enable copyable boxed wrapper
|
||||
// ideally only used in case where the copy is (equivalently);
|
||||
// * cheap
|
||||
// * does not change the underlying pointer
|
||||
// * refcount based
|
||||
// * has no semantics effects
|
||||
// (e.g. GstMiniObject refcount affects writable)
|
||||
template<typename CType>
|
||||
struct enable_boxed_copy :
|
||||
#if GI_ENABLE_BOXED_COPY_ALL
|
||||
public std::true_type
|
||||
#else
|
||||
public std::false_type
|
||||
#endif
|
||||
{};
|
||||
|
||||
} // namespace repository
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// class tags
|
||||
class Boxed
|
||||
{};
|
||||
class CBoxed : public Boxed
|
||||
{};
|
||||
class GBoxed : public Boxed
|
||||
{};
|
||||
|
||||
template<typename CType>
|
||||
class SharedWrapper
|
||||
{
|
||||
public:
|
||||
typedef SharedWrapper self;
|
||||
|
||||
protected:
|
||||
CType *data_ = nullptr;
|
||||
|
||||
public:
|
||||
CType *gobj_() { return data_; }
|
||||
const CType *gobj_() const { return data_; }
|
||||
|
||||
explicit operator bool() const { return (bool)data_; }
|
||||
|
||||
bool operator==(const SharedWrapper &other) const
|
||||
{
|
||||
return data_ == other.data_;
|
||||
}
|
||||
|
||||
bool operator==(std::nullptr_t o) const { return data_ == o; }
|
||||
|
||||
bool operator!=(const SharedWrapper &other) const
|
||||
{
|
||||
return data_ != other.data_;
|
||||
}
|
||||
|
||||
bool operator!=(std::nullptr_t o) const { return data_ != o; }
|
||||
|
||||
CType *release_()
|
||||
{
|
||||
auto tmp = this->data_;
|
||||
this->data_ = nullptr;
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename CppType, typename CType>
|
||||
struct GBoxedFuncs
|
||||
{
|
||||
static CType *copy(const void *data)
|
||||
{
|
||||
return (CType *)g_boxed_copy(CppType::get_type_(), data);
|
||||
}
|
||||
static void free(void *data) { g_boxed_free(CppType::get_type_(), data); }
|
||||
};
|
||||
|
||||
struct CBoxedFuncsBase
|
||||
{
|
||||
static void free(void *data) { g_free(data); }
|
||||
};
|
||||
|
||||
template<typename CppType, typename CType>
|
||||
struct CBoxedFuncs : CBoxedFuncsBase
|
||||
{
|
||||
static CType *copy(const void *data)
|
||||
{
|
||||
#if GLIB_CHECK_VERSION(2, 68, 0)
|
||||
return (CType *)g_memdup2(data, sizeof(CType));
|
||||
#else
|
||||
return (CType *)g_memdup(data, sizeof(CType));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<typename CType, typename Funcs, typename TagType>
|
||||
class BoxedWrapper : public SharedWrapper<CType>, public TagType
|
||||
{
|
||||
typedef BoxedWrapper self;
|
||||
|
||||
protected:
|
||||
static void _deleter(CType *obj, ...)
|
||||
{
|
||||
if (obj)
|
||||
Funcs::free(obj);
|
||||
}
|
||||
|
||||
static CType *_copy(const CType *obj)
|
||||
{
|
||||
return obj ? Funcs::copy(obj) : nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
typedef CType BaseObjectType;
|
||||
|
||||
BoxedWrapper(CType *obj = nullptr) noexcept { this->data_ = obj; }
|
||||
|
||||
CType *gobj_copy_() const { return _copy(this->gobj_()); }
|
||||
|
||||
// resulting casted type determines ownership
|
||||
template<typename Cpp>
|
||||
static Cpp wrap(const typename Cpp::BaseObjectType *obj)
|
||||
{
|
||||
static_assert(sizeof(Cpp) == sizeof(self), "type wrap not supported");
|
||||
static_assert(std::is_base_of<self, Cpp>::value, "type wrap not supported");
|
||||
BoxedWrapper w(const_cast<typename Cpp::BaseObjectType *>(obj));
|
||||
return std::move(*static_cast<Cpp *>(&w));
|
||||
}
|
||||
};
|
||||
|
||||
// in templates below, Base should be a subclass of BoxedWrapper
|
||||
// so we re-use the members it provides, as well as the wrap template
|
||||
// to avoid ambiguous reference to the latter
|
||||
// (if BoxedWrapper were inherited from again)
|
||||
|
||||
// the nullptr_t constructor (indirectly) supports `= nullptr` (assignment)
|
||||
|
||||
template<typename CppType, typename CType>
|
||||
using GBoxedWrapperBase =
|
||||
BoxedWrapper<CType, GBoxedFuncs<CppType, CType>, GBoxed>;
|
||||
|
||||
// assuming Base has suitable members such as above BoxedWrapper
|
||||
template<typename Base>
|
||||
class MoveWrapper : public Base
|
||||
{
|
||||
typedef Base super;
|
||||
|
||||
public:
|
||||
using super::super;
|
||||
|
||||
~MoveWrapper() { this->_deleter(this->data_, static_cast<Base *>(this)); }
|
||||
|
||||
MoveWrapper(const MoveWrapper &) = delete;
|
||||
MoveWrapper &operator=(const MoveWrapper &) = delete;
|
||||
|
||||
MoveWrapper(MoveWrapper &&o) noexcept { *this = std::move(o); }
|
||||
MoveWrapper &operator=(MoveWrapper &&o) noexcept
|
||||
{
|
||||
if (this != &o) {
|
||||
this->_deleter(this->data_, static_cast<Base *>(this));
|
||||
(Base &)(*this) = std::move(o);
|
||||
o.data_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Base>
|
||||
class CopyWrapper : public MoveWrapper<Base>
|
||||
{
|
||||
typedef MoveWrapper<Base> super;
|
||||
|
||||
public:
|
||||
using super::super;
|
||||
|
||||
CopyWrapper(const CopyWrapper &o) noexcept : super()
|
||||
{
|
||||
this->data_ = this->_copy(o.data_);
|
||||
}
|
||||
CopyWrapper &operator=(const CopyWrapper &o) noexcept
|
||||
{
|
||||
if (this != &o) {
|
||||
this->_deleter(this->data_, static_cast<Base *>(this));
|
||||
if (sizeof(Base) > sizeof(this->data_))
|
||||
(Base &)(*this) = std::move(o);
|
||||
this->data_ = this->_copy(o.data_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CopyWrapper(CopyWrapper &&o) noexcept = default;
|
||||
CopyWrapper &operator=(CopyWrapper &&o) noexcept = default;
|
||||
|
||||
// also accept from corresponding Reference (also based on Base)
|
||||
CopyWrapper(const Base &o) noexcept : super()
|
||||
{
|
||||
this->data_ = this->_copy(((CopyWrapper &)o).data_);
|
||||
}
|
||||
CopyWrapper(Base &&o) noexcept : super()
|
||||
{
|
||||
(super &)(*this) = std::move((super &)o);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename CType, typename Base>
|
||||
using SelectWrapper =
|
||||
typename std::conditional<repository::enable_boxed_copy<CType>::value,
|
||||
CopyWrapper<Base>, MoveWrapper<Base>>::type;
|
||||
|
||||
// basis for registered boxed types
|
||||
template<typename CppType, typename CType,
|
||||
typename Base = GBoxedWrapperBase<CppType, CType>, typename RefType = void>
|
||||
class GBoxedWrapper : public SelectWrapper<CType, Base>
|
||||
{
|
||||
typedef SelectWrapper<CType, Base> super;
|
||||
|
||||
public:
|
||||
typedef RefType ReferenceType;
|
||||
|
||||
using super::super;
|
||||
|
||||
GBoxedWrapper(std::nullptr_t = nullptr) {}
|
||||
|
||||
void allocate_()
|
||||
{
|
||||
if (this->data_)
|
||||
return;
|
||||
// make sure we match boxed allocation with boxed free
|
||||
// still guessing here that all-0 makes for a decent init :-(
|
||||
CType tmp;
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
this->data_ = (CType *)g_boxed_copy(this->get_type_(), &tmp);
|
||||
}
|
||||
|
||||
// essentially g_boxed_copy
|
||||
CppType copy_() const
|
||||
{
|
||||
return super::template wrap<CppType>(this->gobj_copy_());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename CppType, typename CType>
|
||||
using CBoxedWrapperBase =
|
||||
BoxedWrapper<CType, CBoxedFuncs<CppType, CType>, CBoxed>;
|
||||
|
||||
// basis for non-registered plain C boxed type
|
||||
template<typename CppType, typename CType,
|
||||
typename Base = CBoxedWrapperBase<CppType, CType>, typename RefType = void>
|
||||
class CBoxedWrapper : public MoveWrapper<Base>
|
||||
{
|
||||
typedef Base super;
|
||||
|
||||
public:
|
||||
typedef RefType ReferenceType;
|
||||
|
||||
CBoxedWrapper(std::nullptr_t = nullptr) {}
|
||||
|
||||
void allocate_()
|
||||
{
|
||||
if (this->data_)
|
||||
return;
|
||||
this->data_ = g_new0(CType, 1);
|
||||
}
|
||||
|
||||
static CppType new_()
|
||||
{
|
||||
return super::template wrap<CppType>(g_new0(CType, 1));
|
||||
}
|
||||
};
|
||||
|
||||
// allocate helper;
|
||||
// dispatch to method if available
|
||||
template<typename T, typename Enable = void>
|
||||
struct allocator : public std::false_type
|
||||
{
|
||||
static void allocate(T &) {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct allocator<T, decltype(T().allocate_())> : public std::true_type
|
||||
{
|
||||
static void allocate(T &v) { v.allocate_(); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
allocate(T &v)
|
||||
{
|
||||
allocator<T>::allocate(v);
|
||||
}
|
||||
|
||||
// basis for ref-holding to registered box type
|
||||
template<typename CppType, typename CType, typename Base>
|
||||
class GBoxedRefWrapper : public Base
|
||||
{
|
||||
typedef Base super;
|
||||
|
||||
public:
|
||||
typedef CppType BoxType;
|
||||
|
||||
GBoxedRefWrapper(std::nullptr_t = nullptr) {}
|
||||
|
||||
// construct from owning version, but not the other way around
|
||||
// (which requires an explicit copy)
|
||||
GBoxedRefWrapper(const CppType &other)
|
||||
{
|
||||
(super &)(*this) = super::template wrap<super>(other.gobj_());
|
||||
}
|
||||
|
||||
// support way to convert to owning box
|
||||
// (by means of copy as opposed to a simple ref)
|
||||
CppType copy_() const
|
||||
{
|
||||
return super::template wrap<CppType>(this->gobj_copy_());
|
||||
}
|
||||
};
|
||||
|
||||
// basis for ref-holding to non-registered plain C boxed type
|
||||
template<typename CppType, typename CType, typename Base>
|
||||
class CBoxedRefWrapper : public Base
|
||||
{
|
||||
typedef Base super;
|
||||
|
||||
public:
|
||||
typedef CppType BoxType;
|
||||
|
||||
CBoxedRefWrapper(std::nullptr_t = nullptr) {}
|
||||
|
||||
// construct from owning version, but not the other way around
|
||||
// (which requires an explicit copy)
|
||||
CBoxedRefWrapper(const CppType &other)
|
||||
{
|
||||
(super &)(*this) = super::template wrap<super>(other.gobj_());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace gi
|
||||
|
||||
#endif // GI_BOXED_HPP
|
||||
960
cmake/external/glib/cppgir/gi/callback.hpp
vendored
Normal file
960
cmake/external/glib/cppgir/gi/callback.hpp
vendored
Normal file
@@ -0,0 +1,960 @@
|
||||
#ifndef GI_CALLBACK_HPP
|
||||
#define GI_CALLBACK_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
#include "exception.hpp"
|
||||
#include "wrap.hpp"
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
|
||||
inline ::GError **
|
||||
find_gerror(bool &has_gerror)
|
||||
{
|
||||
has_gerror = false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline ::GError **
|
||||
find_gerror(bool &has_gerror, ::GError **error)
|
||||
{
|
||||
has_gerror = true;
|
||||
return error;
|
||||
}
|
||||
|
||||
template<typename CT, typename... CType>
|
||||
inline ::GError **
|
||||
find_gerror(bool &has_gerror, CT /*arg*/, CType... args)
|
||||
{
|
||||
return find_gerror(has_gerror, args...);
|
||||
}
|
||||
|
||||
inline ::GError *
|
||||
exception_error(const repository::GLib::Error &exc)
|
||||
{
|
||||
return g_error_copy(exc.gobj_());
|
||||
}
|
||||
|
||||
inline ::GError *
|
||||
exception_error(const repository::GLib::Error_Ref &exc)
|
||||
{
|
||||
return g_error_copy(exc.gobj_());
|
||||
}
|
||||
|
||||
template<typename E>
|
||||
inline ::GError *
|
||||
exception_error(const E &exc)
|
||||
{
|
||||
static auto quark = g_quark_from_static_string("gi-error-quark");
|
||||
return g_error_new(quark, 0, "%s", exception_desc(exc).c_str());
|
||||
}
|
||||
|
||||
template<bool SILENT = FALSE, typename E, typename... CType>
|
||||
void
|
||||
report_exception(const E &exc, CType... args)
|
||||
{
|
||||
// see if we can really report error somewhere
|
||||
bool has_gerror = false;
|
||||
GError **error = find_gerror(has_gerror, args...);
|
||||
if (has_gerror) {
|
||||
g_return_if_fail(error == NULL || *error == NULL);
|
||||
if (error)
|
||||
*error = exception_error(exc);
|
||||
// if caller does not need/want error, exception disappears here
|
||||
} else {
|
||||
// simply report the hard and simple way
|
||||
// otherwise catch internally if something else/more is desired
|
||||
if (!SILENT) {
|
||||
auto msg = std::string("handler exception; ") + exception_desc(exc);
|
||||
g_critical("%s", msg.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// (re)float only applies to GObject
|
||||
template<typename CppType, typename Transfer,
|
||||
typename std::enable_if<!traits::is_object<CppType>::value>::type * =
|
||||
nullptr>
|
||||
auto
|
||||
unwrap_maybe_float(CppType &&v, const Transfer &t)
|
||||
{
|
||||
return unwrap(std::forward<CppType>(v), t);
|
||||
}
|
||||
|
||||
// (re) float only for transfer none/floating
|
||||
template<typename CppType,
|
||||
typename std::enable_if<traits::is_object<CppType>::value>::type * =
|
||||
nullptr>
|
||||
inline typename traits::ctype<CppType>::type
|
||||
unwrap_maybe_float(CppType &&v, const transfer_full_t &t)
|
||||
{
|
||||
return unwrap(std::forward<CppType>(v), t);
|
||||
}
|
||||
|
||||
template<typename CppType,
|
||||
typename std::enable_if<traits::is_object<CppType>::value>::type * =
|
||||
nullptr>
|
||||
inline typename traits::ctype<CppType>::type
|
||||
unwrap_maybe_float(CppType &&v, const transfer_none_t &)
|
||||
{
|
||||
// expected called with r-value
|
||||
static_assert(!std::is_reference<CppType>::value, "");
|
||||
// steal/take from wrapper
|
||||
auto result = (typename traits::ctype<CppType>::type)v.release_();
|
||||
// the following is essentially a bit of a hack as mentioned in
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=693393)
|
||||
// that is, we are about to return an object to C (from binding/callback)
|
||||
// and this should be done with none transfer
|
||||
// this none may actually mean floating (e.g. a factory-like callback)
|
||||
// but no way to know from annotations
|
||||
// so if at runtime the wrapper actually holds the only reference,
|
||||
// then it is about to be destroyed (when wrapper goes away)
|
||||
// *before* the object can make it back to caller
|
||||
// so turn that ref into a floating one (as only that makes sense)
|
||||
// if it is not the only ref, it is kept alive elsewhere
|
||||
// (as typically so for a "getter" callback)
|
||||
// so it is really treated as none
|
||||
auto obj = (GObject *)result;
|
||||
// theoretically not MT safe, but if == 1, only 1 thread should be involved
|
||||
if (obj->ref_count == 1) {
|
||||
g_object_force_floating(obj);
|
||||
} else {
|
||||
// otherwise unref as wrapper would have
|
||||
g_object_unref(obj);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T
|
||||
unconst(T t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
inline char *
|
||||
unconst(const char *t)
|
||||
{
|
||||
return (char *)t;
|
||||
}
|
||||
|
||||
// helper types to provide additional argument info beyond Transfer
|
||||
template<std::size_t... index>
|
||||
struct args_index
|
||||
{
|
||||
static constexpr auto value = std::make_tuple(index...);
|
||||
using value_type = decltype(value);
|
||||
};
|
||||
|
||||
template<typename Transfer, bool _inout, typename CustomTraits = void,
|
||||
typename ArgsIndex = args_index<>>
|
||||
struct arg_info
|
||||
{
|
||||
using transfer_type = Transfer;
|
||||
static constexpr bool inout = _inout;
|
||||
// tuple of index; used to selects the C arguments (to assemble C++ argument)
|
||||
using args_type = ArgsIndex;
|
||||
// additional info as used by corresponding cb_arg_handler
|
||||
using custom_type = CustomTraits;
|
||||
};
|
||||
|
||||
// access above info by forwarding types
|
||||
template<typename T, typename Enable = void>
|
||||
struct arg_traits
|
||||
{
|
||||
using transfer_type = typename T::transfer_type;
|
||||
static constexpr bool inout = T::inout;
|
||||
using args_type = typename T::args_type;
|
||||
using custom_type = typename T::custom_type;
|
||||
};
|
||||
|
||||
// legacy case; only transfer type
|
||||
template<typename T>
|
||||
struct arg_traits<T,
|
||||
typename std::enable_if<std::is_base_of<transfer_t, T>::value>::type>
|
||||
{
|
||||
using transfer_type = T;
|
||||
static constexpr bool inout = false;
|
||||
using args_type = args_index<>;
|
||||
using custom_type = void;
|
||||
};
|
||||
|
||||
// IndexTuple is essentially an args_index<...>
|
||||
template<typename IndexTuple, std::size_t... Index, typename F,
|
||||
typename ArgTuple>
|
||||
decltype(auto)
|
||||
apply_with_args(std::index_sequence<Index...>, F &&f, ArgTuple &&args)
|
||||
{
|
||||
return f(std::get<std::get<Index>(IndexTuple::value)>(args)...);
|
||||
}
|
||||
|
||||
template<typename IndexTuple, typename F, typename... Args>
|
||||
decltype(auto)
|
||||
apply_with_args(F &&f, Args &&...args)
|
||||
{
|
||||
return apply_with_args<IndexTuple>(
|
||||
std::make_index_sequence<
|
||||
std::tuple_size<typename IndexTuple::value_type>::value>(),
|
||||
std::forward<F>(f), std::forward_as_tuple(args...));
|
||||
}
|
||||
|
||||
// a simple callback has no (need for) args_index
|
||||
template<typename T>
|
||||
struct is_simple_cb : public std::true_type
|
||||
{};
|
||||
|
||||
template<typename Transfer, typename... Transfers>
|
||||
struct is_simple_cb<std::tuple<Transfer, Transfers...>>
|
||||
{
|
||||
static constexpr bool value =
|
||||
std::tuple_size<
|
||||
typename arg_traits<Transfer>::args_type::value_type>::value == 0 ||
|
||||
(sizeof...(Transfers) > 0 &&
|
||||
is_simple_cb<std::tuple<Transfers...>>::value);
|
||||
};
|
||||
|
||||
template<typename T, typename CT = void>
|
||||
struct map_cpp_function;
|
||||
|
||||
// handles all calls C -> C++ (callbacks, virtual method calls)
|
||||
// restrictions though on types supported (enforced by code generation)
|
||||
template<typename T, typename RetTransfer, typename ArgTransfers,
|
||||
typename CT = typename map_cpp_function<T>::type,
|
||||
bool SIMPLE = is_simple_cb<ArgTransfers>::value>
|
||||
struct transform_caller;
|
||||
|
||||
// helper used below that provides pre-call and post-call steps
|
||||
// to handle each argument's conversion to and from C++
|
||||
// in so-called (most) simple cases, there is one-to-one mapping between
|
||||
// C and C++ arguments and C++ argument type that allows to deduce context
|
||||
// (in particular, no callbacks or sized array)
|
||||
// as such, proper steps can be obtained by specialization on Cpp argument type
|
||||
template<typename CppArg, typename Enable = void>
|
||||
struct cb_arg_handler;
|
||||
|
||||
// in simple cases, C signature can be derived from C++ signature
|
||||
template<typename T, typename CT>
|
||||
struct map_cpp_function
|
||||
{
|
||||
using type = CT;
|
||||
};
|
||||
|
||||
template<typename R, typename... Args>
|
||||
struct map_cpp_function<R(Args...), void>
|
||||
{
|
||||
using type = typename traits::ctype<R>::type(
|
||||
typename cb_arg_handler<Args>::c_type...);
|
||||
};
|
||||
|
||||
// signature used in virtual method handling
|
||||
template<typename R, typename... Args>
|
||||
struct map_cpp_function<R (*)(Args...), void>
|
||||
{
|
||||
using type = typename traits::ctype<R>::type(
|
||||
typename cb_arg_handler<Args>::c_type...);
|
||||
};
|
||||
|
||||
// NOTE function type R(const A) is identical to R(A)
|
||||
// so no deduced Args below will retain const (if such)
|
||||
// in simple cases, *Transfer* is simply a transfer type
|
||||
// but it may also be a more elaborate argument trait
|
||||
|
||||
template<typename R, typename... Args, typename RetTransfer,
|
||||
typename... Transfers, typename CR, typename... CArgs, bool SIMPLE>
|
||||
struct transform_caller<R(Args...), RetTransfer, std::tuple<Transfers...>,
|
||||
CR(CArgs...), SIMPLE>
|
||||
{
|
||||
static_assert(sizeof...(Args) == sizeof...(Transfers), "");
|
||||
static_assert(!SIMPLE || sizeof...(Args) == sizeof...(CArgs), "");
|
||||
|
||||
typedef transform_caller self_type;
|
||||
typedef R (*caller_type)(Args &&..., void *d);
|
||||
|
||||
private:
|
||||
static R do_call(Args &&...args, caller_type func, void *d)
|
||||
{
|
||||
return func(std::forward<Args>(args)..., d);
|
||||
}
|
||||
|
||||
// helper that provides context for pack expansion below
|
||||
static void dummy_call(...){};
|
||||
|
||||
template<typename T>
|
||||
static auto _tt(const T &)
|
||||
{
|
||||
return typename arg_traits<T>::transfer_type();
|
||||
}
|
||||
|
||||
// non-void return
|
||||
template<typename T, std::size_t... Index,
|
||||
typename std::enable_if<SIMPLE && !std::is_void<T>::value>::type * =
|
||||
nullptr>
|
||||
static CR _wrapper(
|
||||
CArgs... args, caller_type func, void *d, std::index_sequence<Index...>)
|
||||
{
|
||||
std::tuple<cb_arg_handler<Args>...> handlers;
|
||||
auto ret = do_call(
|
||||
std::get<Index>(handlers).arg(args, _tt(Transfers()), Transfers())...,
|
||||
func, d);
|
||||
dummy_call((std::get<Index>(handlers).post(args, _tt(Transfers())), 0)...);
|
||||
return unconst(unwrap_maybe_float(std::move(ret), RetTransfer()));
|
||||
}
|
||||
|
||||
// void return
|
||||
template<typename T, std::size_t... Index,
|
||||
typename std::enable_if<SIMPLE && std::is_void<T>::value>::type * =
|
||||
nullptr>
|
||||
static CR _wrapper(
|
||||
CArgs... args, caller_type func, void *d, std::index_sequence<Index...>)
|
||||
{
|
||||
std::tuple<cb_arg_handler<Args>...> handlers;
|
||||
do_call(
|
||||
std::get<Index>(handlers).arg(args, _tt(Transfers()), Transfers())...,
|
||||
func, d);
|
||||
dummy_call((std::get<Index>(handlers).post(args, _tt(Transfers())), 0)...);
|
||||
}
|
||||
|
||||
// complex; non-void return
|
||||
template<typename T, std::size_t... Index,
|
||||
typename std::enable_if<!SIMPLE && !std::is_void<T>::value>::type * =
|
||||
nullptr>
|
||||
static CR _wrapper(
|
||||
CArgs... args, caller_type func, void *d, std::index_sequence<Index...>)
|
||||
{
|
||||
std::tuple<cb_arg_handler<Args>...> handlers;
|
||||
auto ret = do_call(
|
||||
apply_with_args<typename arg_traits<Transfers>::args_type>(
|
||||
[&handlers](auto... selargs) mutable -> decltype(auto) {
|
||||
return std::get<Index>(handlers).arg(selargs...,
|
||||
typename arg_traits<Transfers>::transfer_type(), Transfers());
|
||||
},
|
||||
args...)...,
|
||||
func, d);
|
||||
dummy_call((apply_with_args<typename arg_traits<Transfers>::args_type>(
|
||||
[&handlers](auto... selargs) mutable -> decltype(auto) {
|
||||
return std::get<Index>(handlers).post(selargs...,
|
||||
typename arg_traits<Transfers>::transfer_type());
|
||||
},
|
||||
args...),
|
||||
0)...);
|
||||
return unconst(unwrap_maybe_float(std::move(ret), RetTransfer()));
|
||||
}
|
||||
|
||||
// complex; void return
|
||||
template<typename T, std::size_t... Index,
|
||||
typename std::enable_if<!SIMPLE && std::is_void<T>::value>::type * =
|
||||
nullptr>
|
||||
static CR _wrapper(
|
||||
CArgs... args, caller_type func, void *d, std::index_sequence<Index...>)
|
||||
{
|
||||
std::tuple<cb_arg_handler<Args>...> handlers;
|
||||
do_call(apply_with_args<typename arg_traits<Transfers>::args_type>(
|
||||
[&handlers](auto... selargs) mutable -> decltype(auto) {
|
||||
return std::get<Index>(handlers).arg(selargs...,
|
||||
typename arg_traits<Transfers>::transfer_type(),
|
||||
Transfers());
|
||||
},
|
||||
args...)...,
|
||||
func, d);
|
||||
dummy_call((apply_with_args<typename arg_traits<Transfers>::args_type>(
|
||||
[&handlers](auto... selargs) mutable -> decltype(auto) {
|
||||
return std::get<Index>(handlers).post(selargs...,
|
||||
typename arg_traits<Transfers>::transfer_type());
|
||||
},
|
||||
args...),
|
||||
0)...);
|
||||
}
|
||||
|
||||
public:
|
||||
static CR wrapper(CArgs... args, caller_type func, void *d)
|
||||
{
|
||||
// exceptions should not escape into plain C
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
return self_type::template _wrapper<R>(
|
||||
args..., func, d, std::make_index_sequence<sizeof...(Args)>());
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
} catch (const repository::GLib::Error &exc) {
|
||||
report_exception(exc, args...);
|
||||
} catch (const repository::GLib::Error_Ref &exc) {
|
||||
report_exception(exc, args...);
|
||||
} catch (const std::exception &exc) {
|
||||
report_exception(exc, args...);
|
||||
} catch (...) {
|
||||
report_exception(nullptr, args...);
|
||||
}
|
||||
return typename traits::ctype<R>::type();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
// minor helper traits used below
|
||||
namespace _traits
|
||||
{
|
||||
template<typename T>
|
||||
struct remove_all_pointers
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_all_pointers<T *>
|
||||
{
|
||||
using type = typename remove_all_pointers<T>::type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_all_pointers<T *const>
|
||||
{
|
||||
using type = typename remove_all_pointers<T>::type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using is_basic_or_void = typename std::conditional<traits::is_basic<T>::value ||
|
||||
std::is_void<T>::value,
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
template<typename T>
|
||||
using is_basic_argument = typename std::conditional<
|
||||
is_basic_or_void<typename std::remove_cv<
|
||||
typename remove_all_pointers<T>::type>::type>::value,
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
template<typename T>
|
||||
using is_const_lvalue = typename std::conditional<
|
||||
std::is_lvalue_reference<T>::value &&
|
||||
std::is_const<typename std::remove_reference<T>::type>::value,
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
} // namespace _traits
|
||||
|
||||
// generic fallback case; assume input parameter
|
||||
// (also covers boxed callerallocates, which pretty much is/becomes input)
|
||||
template<typename CppArg, typename Enable>
|
||||
struct cb_arg_handler
|
||||
{
|
||||
using c_type = typename traits::ctype<CppArg>::type;
|
||||
|
||||
// use generic CType to handle const differences wrt c_type
|
||||
template<typename CType, typename Transfer, typename ArgTrait>
|
||||
typename std::decay<CppArg>::type arg(
|
||||
CType arg, const Transfer &t, const ArgTrait &) noexcept
|
||||
{
|
||||
// wrap to normalized destination target (no const &)
|
||||
// the destination type here is really only relevant for collection cases
|
||||
// (since that depends on the contained element)
|
||||
// otherwise all the info is pretty much in c_type type
|
||||
return wrap_to<typename std::decay<CppArg>::type>(arg, t);
|
||||
}
|
||||
|
||||
// minor variation; dynamic sized array input
|
||||
// also accepts length input
|
||||
template<typename CType, typename Transfer, typename ArgTrait>
|
||||
typename std::decay<CppArg>::type arg(
|
||||
CType arg, int length, const Transfer &t, const ArgTrait &) noexcept
|
||||
{
|
||||
// destination also really needed here
|
||||
return wrap_to<typename std::decay<CppArg>::type>(arg, length, t);
|
||||
}
|
||||
|
||||
void post(...){};
|
||||
};
|
||||
|
||||
// passthrough on (pointers to) basic values;
|
||||
// handles input/output of basic values, as well as arrays of such
|
||||
template<typename CppArg>
|
||||
struct cb_arg_handler<CppArg,
|
||||
typename std::enable_if<_traits::is_basic_argument<CppArg>::value>::type>
|
||||
{
|
||||
using c_type = CppArg;
|
||||
|
||||
template<typename CType, typename Transfer, typename ArgTrait>
|
||||
CType arg(CType arg, const Transfer &, const ArgTrait &) noexcept
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
void post(...){};
|
||||
|
||||
// array size case; inout C int* to in Cpp int
|
||||
// (only enable if so tagged by non-default custom trait type)
|
||||
template<typename CType, typename Transfer, typename ArgTrait>
|
||||
typename std::enable_if<
|
||||
!std::is_void<typename arg_traits<ArgTrait>::custom_type>::value,
|
||||
CType>::type
|
||||
arg(CType *arg, const Transfer &, const ArgTrait &) noexcept
|
||||
{
|
||||
return *arg;
|
||||
}
|
||||
};
|
||||
|
||||
// handle "complex" (in)out argument
|
||||
// (though also handles e.g. int& case, which might be optimized, but anyways)
|
||||
// these are recognized/assumed to be a pointer or reference to non-basic type
|
||||
template<typename CppArg>
|
||||
struct cb_arg_handler<CppArg,
|
||||
typename std::enable_if<
|
||||
!_traits::is_basic_argument<CppArg>::value &&
|
||||
(std::is_pointer<CppArg>::value ||
|
||||
(std::is_lvalue_reference<CppArg>::value &&
|
||||
!_traits::is_const_lvalue<CppArg>::value))>::type>
|
||||
{
|
||||
using BaseCppType =
|
||||
typename std::decay<typename std::remove_pointer<CppArg>::type>::type;
|
||||
// no more pointer expected here
|
||||
// (other than for void* cases, which do not introspect well, but anyways)
|
||||
static_assert(!std::is_pointer<BaseCppType>::value ||
|
||||
std::is_same<BaseCppType, gpointer>::value ||
|
||||
std::is_same<BaseCppType, gconstpointer>::value,
|
||||
"");
|
||||
using c_type = typename traits::ctype<BaseCppType>::type *;
|
||||
|
||||
// intermediate helper storage
|
||||
BaseCppType var_{};
|
||||
|
||||
// pointer case
|
||||
CppArg rv(std::true_type) { return &var_; }
|
||||
// ref case
|
||||
CppArg rv(std::false_type) { return var_; }
|
||||
|
||||
// handle const variations (e.g. const char**)
|
||||
template<typename T, typename X>
|
||||
static void assign(T *&t, X val)
|
||||
{
|
||||
t = const_cast<T *>(val);
|
||||
}
|
||||
|
||||
template<typename T, typename X>
|
||||
static void assign(T &t, X val)
|
||||
{
|
||||
t = unconst(val);
|
||||
}
|
||||
|
||||
template<typename Transfer, typename ArgTrait>
|
||||
CppArg arg(c_type arg, const Transfer &t, const ArgTrait &)
|
||||
{
|
||||
bool inout = arg_traits<ArgTrait>::inout;
|
||||
// a plain type has no special needs
|
||||
// so we can always take any bogus value as-is
|
||||
// (which is then less sensitive to incorrect annotation)
|
||||
if (arg && (inout || traits::is_plain<BaseCppType>::value))
|
||||
var_ = wrap_to<BaseCppType>(*arg, t);
|
||||
return rv(std::is_pointer<CppArg>());
|
||||
}
|
||||
|
||||
// overall function call happens following arg call above
|
||||
// post invoked after function call
|
||||
template<typename Transfer>
|
||||
void post(c_type arg, const Transfer &t)
|
||||
{
|
||||
if (arg)
|
||||
assign(*arg, unwrap_maybe_float(std::move(var_), t));
|
||||
}
|
||||
|
||||
// sized array variants; arguments (data, size)
|
||||
// latter could be input (int) or (in)out (int*)
|
||||
template<typename Transfer, typename Int, typename ArgTrait>
|
||||
CppArg arg(c_type arg, Int size, const Transfer &t, const ArgTrait &)
|
||||
{
|
||||
bool inout = arg_traits<ArgTrait>::inout;
|
||||
if (arg && inout)
|
||||
var_ = wrap_to<BaseCppType>(*arg, size, t);
|
||||
return rv(std::is_pointer<CppArg>());
|
||||
}
|
||||
|
||||
template<typename Transfer, typename Int, typename ArgTrait>
|
||||
CppArg arg(c_type arg, Int *size, const Transfer &t, const ArgTrait &)
|
||||
{
|
||||
bool inout = arg_traits<ArgTrait>::inout;
|
||||
if (arg && size && inout)
|
||||
var_ = wrap_to<BaseCppType>(*arg, *size, t);
|
||||
return rv(std::is_pointer<CppArg>());
|
||||
}
|
||||
|
||||
template<typename Transfer, typename Int>
|
||||
void post(c_type arg, Int, const Transfer &t)
|
||||
{
|
||||
post(arg, nullptr, t);
|
||||
}
|
||||
|
||||
template<typename Transfer, typename Int>
|
||||
void post(c_type arg, Int *size, const Transfer &t)
|
||||
{
|
||||
if (arg)
|
||||
assign(*arg, unwrap_maybe_float(std::move(var_), t));
|
||||
if (size)
|
||||
*size = var_.size();
|
||||
}
|
||||
};
|
||||
|
||||
// handles callback argument
|
||||
// (as argument in anther callback, most likely a virtual method)
|
||||
template<typename CppArg>
|
||||
struct cb_arg_handler<CppArg,
|
||||
typename std::enable_if<
|
||||
std::is_pointer<typename CppArg::cfunction_type>::value>::type>
|
||||
{
|
||||
template<typename CType, typename Transfer, typename ArgTrait>
|
||||
CppArg arg(CType cb, gpointer userdata, ::GDestroyNotify destroy,
|
||||
const Transfer &, const ArgTrait &) noexcept
|
||||
{
|
||||
using ct = typename arg_traits<ArgTrait>::custom_type;
|
||||
// setup shared pointer to userdata with destroy as Deleter
|
||||
auto sp = destroy ? std::shared_ptr<void>(userdata, destroy) : nullptr;
|
||||
// keep userdata/destroy alive as long as lambda handler
|
||||
auto h = [cb, userdata, sp = std::move(sp)](
|
||||
auto &&...args) -> decltype(auto) {
|
||||
// original callback type CType should match deduced handler_cb_tpe
|
||||
// but let's make sure as usual
|
||||
return ct::handler(std::forward<decltype(args)>(args)...,
|
||||
typename ct::handler_cb_type(cb), userdata);
|
||||
};
|
||||
return h;
|
||||
}
|
||||
|
||||
template<typename CType, typename Transfer, typename ArgTrait>
|
||||
CppArg arg(CType cb, gpointer userdata, const Transfer &t,
|
||||
const ArgTrait &tt) noexcept
|
||||
{
|
||||
return arg(cb, userdata, nullptr, t, tt);
|
||||
}
|
||||
|
||||
void post(...){};
|
||||
};
|
||||
|
||||
class connection_status
|
||||
{
|
||||
public:
|
||||
struct data
|
||||
{
|
||||
bool connected{false};
|
||||
};
|
||||
|
||||
bool connected() const
|
||||
{
|
||||
auto sp = data_.lock();
|
||||
return sp && sp->connected;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::weak_ptr<data> data_;
|
||||
};
|
||||
|
||||
// callback handling
|
||||
template<typename G>
|
||||
class connectable;
|
||||
|
||||
template<typename G, bool AUTODESTROY = false>
|
||||
class callback_wrapper;
|
||||
|
||||
template<typename R, typename... Args>
|
||||
class connectable<R(Args...)>
|
||||
{
|
||||
friend class callback_wrapper<R(Args...)>;
|
||||
struct data : public connection_status::data
|
||||
{
|
||||
template<typename T,
|
||||
typename Enable = typename detail::disable_if_same_or_derived<data, T>>
|
||||
data(T &&t) : callable(std::forward<T>(t))
|
||||
{}
|
||||
|
||||
std::function<R(Args... args)> callable;
|
||||
};
|
||||
|
||||
struct connection_status_factory : public connection_status
|
||||
{
|
||||
connection_status_factory(std::shared_ptr<data> p)
|
||||
{
|
||||
data_ = std::weak_ptr<connection_status::data>(p);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
// avoid copy constructor mishaps
|
||||
template<typename T,
|
||||
typename Enable =
|
||||
typename detail::disable_if_same_or_derived<connectable, T>>
|
||||
connectable(T &&t) : data_(std::make_shared<data>(std::forward<T>(t)))
|
||||
{}
|
||||
|
||||
connection_status connection() const
|
||||
{
|
||||
return connection_status_factory(data_);
|
||||
}
|
||||
|
||||
R operator()(Args... args) const
|
||||
{
|
||||
return data_->callable(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
explicit operator bool() const { return data_ && data_->callable; }
|
||||
|
||||
private:
|
||||
// state management by wrapper
|
||||
void connected(bool conn) { data_->connected = conn; }
|
||||
void disconnect() { data_->connected = false; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<data> data_;
|
||||
};
|
||||
|
||||
template<typename R, typename... Args, bool AUTODESTROY>
|
||||
class callback_wrapper<R(Args...), AUTODESTROY>
|
||||
{
|
||||
typedef callback_wrapper self_type;
|
||||
|
||||
public:
|
||||
template<typename T,
|
||||
typename Enable =
|
||||
typename detail::disable_if_same_or_derived<callback_wrapper, T>>
|
||||
callback_wrapper(T &&t) : _callback(std::forward<T>(t))
|
||||
{
|
||||
// mark connected now that it is wrapped
|
||||
_callback.connected(true);
|
||||
}
|
||||
|
||||
// (only) used by manual callback workaround
|
||||
static R wrapper(Args... args, gpointer user_data)
|
||||
{
|
||||
auto t = reinterpret_cast<callback_wrapper *>(user_data);
|
||||
std::unique_ptr<self_type> wt(AUTODESTROY ? t : nullptr);
|
||||
return t->_callback(args...);
|
||||
}
|
||||
|
||||
static void destroy(gpointer user_data)
|
||||
{
|
||||
auto t = reinterpret_cast<callback_wrapper *>(user_data);
|
||||
delete t;
|
||||
}
|
||||
|
||||
// (async scope) wrapper may have to take ownership of additional data
|
||||
// (other callback wrapper)
|
||||
template<typename T>
|
||||
void take_data(std::shared_ptr<T> d)
|
||||
{
|
||||
auto cb = std::move(_callback.data_->callable);
|
||||
auto newcb = [d, cb](Args &&...args) {
|
||||
return cb(std::forward<Args>(args)...);
|
||||
};
|
||||
_callback.data_->callable = std::move(newcb);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void take_data(T *d)
|
||||
{
|
||||
take_data(std::shared_ptr<T>(d));
|
||||
}
|
||||
|
||||
~callback_wrapper()
|
||||
{
|
||||
// other shared ptr to data might be around (unlikely though)
|
||||
// but regardless disconnect now as requested (as wrapper is going away)
|
||||
_callback.disconnect();
|
||||
}
|
||||
|
||||
connectable<R(Args... args)> _callback;
|
||||
};
|
||||
|
||||
template<typename G, typename CG = typename map_cpp_function<G>::type>
|
||||
struct transform_callback_wrapper;
|
||||
|
||||
template<typename R, typename... Args, typename CR, typename... CArgs>
|
||||
struct transform_callback_wrapper<R(Args...), CR(CArgs...)>
|
||||
{
|
||||
// transfers of arguments
|
||||
template<bool AUTODESTROY, typename RetTransfer, typename... Transfers>
|
||||
class with_transfer : public callback_wrapper<R(Args...)>
|
||||
{
|
||||
typedef callback_wrapper<R(Args...)> super_type;
|
||||
typedef with_transfer self_type;
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
explicit with_transfer(T &&t) : super_type(std::forward<T>(t))
|
||||
{}
|
||||
|
||||
private:
|
||||
static R caller(Args &&...args, void *d)
|
||||
{
|
||||
auto this_ = (self_type *)d;
|
||||
return this_->_callback(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
public:
|
||||
static CR wrapper(CArgs... args, gpointer user_data)
|
||||
{
|
||||
auto t = reinterpret_cast<self_type *>(user_data);
|
||||
std::unique_ptr<self_type> wt(AUTODESTROY ? t : nullptr);
|
||||
return transform_caller<R(Args...), RetTransfer, std::tuple<Transfers...>,
|
||||
CR(CArgs...)>::wrapper(args..., caller, t);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// used early in declarations, so avoid using unknown types in CSigOrVoid
|
||||
template<typename CppSig, typename RetTransfer,
|
||||
typename ArgTransfers = std::tuple<>, typename CSigOrVoid = void>
|
||||
class callback;
|
||||
|
||||
template<typename CppSig, typename RetTransfer, typename... Transfers,
|
||||
typename CSigOrVoid>
|
||||
class callback<CppSig, RetTransfer, std::tuple<Transfers...>, CSigOrVoid>
|
||||
: public connectable<CppSig>
|
||||
{
|
||||
typedef connectable<CppSig> super_type;
|
||||
|
||||
public:
|
||||
typedef CppSig function_type;
|
||||
typedef typename map_cpp_function<CppSig, CSigOrVoid>::type CSig;
|
||||
|
||||
template<bool ASYNC = false>
|
||||
using wrapper_type = typename transform_callback_wrapper<function_type,
|
||||
CSig>::template with_transfer<ASYNC, RetTransfer, Transfers...>;
|
||||
|
||||
typedef typename std::add_pointer<decltype(wrapper_type<>::wrapper)>::type
|
||||
cfunction_type;
|
||||
|
||||
using super_type::super_type;
|
||||
};
|
||||
|
||||
// signal handling;
|
||||
// transfer none for arguments
|
||||
// transfer full for return
|
||||
template<typename G>
|
||||
struct transform_signal_wrapper;
|
||||
|
||||
template<typename T>
|
||||
struct signal_arg_transfer
|
||||
{
|
||||
typedef transfer_none_t type;
|
||||
};
|
||||
|
||||
template<typename R, typename... Args>
|
||||
struct transform_signal_wrapper<R(Args...)>
|
||||
: public transform_callback_wrapper<R(
|
||||
Args...)>::template with_transfer<false, transfer_full_t,
|
||||
typename detail::signal_arg_transfer<Args>::type...>
|
||||
{
|
||||
private:
|
||||
typedef typename transform_callback_wrapper<R(
|
||||
Args...)>::template with_transfer<false, transfer_full_t,
|
||||
typename detail::signal_arg_transfer<Args>::type...>
|
||||
super_;
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
transform_signal_wrapper(T &&t) : super_(std::forward<T>(t))
|
||||
{}
|
||||
};
|
||||
|
||||
// connection helpers
|
||||
class connection_impl
|
||||
{
|
||||
public:
|
||||
connection_impl(gulong id, connection_status s) : id_(id), status_(s) {}
|
||||
|
||||
bool connected() const { return status_.connected(); }
|
||||
|
||||
gulong id() const { return id_; }
|
||||
|
||||
protected:
|
||||
gulong id_;
|
||||
connection_status status_;
|
||||
};
|
||||
|
||||
template<typename Connection>
|
||||
class connection
|
||||
{
|
||||
typedef Connection impl;
|
||||
|
||||
public:
|
||||
connection() = default;
|
||||
|
||||
template<typename... Args>
|
||||
explicit connection(Args... arg)
|
||||
: conn_(std::make_shared<impl>(std::forward<Args>(arg)...))
|
||||
{
|
||||
// this is to be expected at this time
|
||||
if (!connected()) {
|
||||
g_warning("creating non-connected connection");
|
||||
}
|
||||
}
|
||||
|
||||
// implicit copy/move
|
||||
|
||||
bool connected() const { return conn_ && conn_->connected(); }
|
||||
void disconnect()
|
||||
{
|
||||
if (connected())
|
||||
conn_->disconnect();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<impl> conn_;
|
||||
};
|
||||
|
||||
template<typename ConnectionBase>
|
||||
class scoped_connection : public ConnectionBase
|
||||
{
|
||||
typedef ConnectionBase connection;
|
||||
|
||||
public:
|
||||
scoped_connection() : connection() {}
|
||||
~scoped_connection() { this->disconnect(); }
|
||||
|
||||
void release() { this->conn_.reset(); }
|
||||
|
||||
// ensure default movable
|
||||
scoped_connection(scoped_connection &&other) = default;
|
||||
scoped_connection &operator=(scoped_connection &&other) = default;
|
||||
|
||||
// not copyable; to avoid inadvertent disconnect
|
||||
scoped_connection(const scoped_connection &other) = delete;
|
||||
scoped_connection &operator=(const scoped_connection &other) = delete;
|
||||
|
||||
// but convert/assign from base class
|
||||
scoped_connection(const connection &other) : connection(other) {}
|
||||
scoped_connection &operator=(const connection &other)
|
||||
{
|
||||
(*(connection *)(this)) = other;
|
||||
return *this;
|
||||
}
|
||||
scoped_connection &operator=(const connection &&other)
|
||||
{
|
||||
(*(connection *)(this)) = std::move(other);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// function bind helpers
|
||||
template<typename R, typename T, typename Tp, typename... Args>
|
||||
inline std::function<R(Args...)>
|
||||
mem_fun(R (T::*pm)(Args...), Tp object)
|
||||
{
|
||||
return [object, pm](Args... args) {
|
||||
return (object->*pm)(std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename R, typename T, typename Tp, typename... Args>
|
||||
inline std::function<R(Args...)>
|
||||
mem_fun(R (T::*pm)(Args...) const, Tp object)
|
||||
{
|
||||
return [object, pm](Args... args) {
|
||||
return (object->*pm)(std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
// expose for use in fallback scenarios
|
||||
using detail::callback_wrapper;
|
||||
|
||||
} // namespace gi
|
||||
|
||||
#endif // CALLBACK_HPP
|
||||
1970
cmake/external/glib/cppgir/gi/container.hpp
vendored
Normal file
1970
cmake/external/glib/cppgir/gi/container.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
268
cmake/external/glib/cppgir/gi/enumflag.hpp
vendored
Normal file
268
cmake/external/glib/cppgir/gi/enumflag.hpp
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
#ifndef GI_ENUMFLAG_HPP
|
||||
#define GI_ENUMFLAG_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
#include "exception.hpp"
|
||||
#include "value.hpp"
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
struct EnumValueTraits
|
||||
{
|
||||
typedef GEnumClass class_type;
|
||||
typedef GEnumValue value_type;
|
||||
|
||||
static class_type *get_class(GType gtype)
|
||||
{
|
||||
auto c = g_type_class_peek(gtype);
|
||||
return (class_type *)(c ? G_ENUM_CLASS(c) : c);
|
||||
}
|
||||
static value_type *get_value(class_type *klass, int v)
|
||||
{
|
||||
return g_enum_get_value(klass, v);
|
||||
}
|
||||
static value_type *get_by_name(class_type *klass, const char *name)
|
||||
{
|
||||
return g_enum_get_value_by_name(klass, name);
|
||||
}
|
||||
static value_type *get_by_nick(class_type *klass, const char *name)
|
||||
{
|
||||
return g_enum_get_value_by_nick(klass, name);
|
||||
}
|
||||
};
|
||||
|
||||
struct FlagsValueTraits
|
||||
{
|
||||
typedef GFlagsClass class_type;
|
||||
typedef GFlagsValue value_type;
|
||||
|
||||
static class_type *get_class(GType gtype)
|
||||
{
|
||||
auto c = g_type_class_peek(gtype);
|
||||
return (class_type *)(c ? G_FLAGS_CLASS(c) : c);
|
||||
}
|
||||
static value_type *get_value(class_type *klass, int v)
|
||||
{
|
||||
return g_flags_get_first_value(klass, v);
|
||||
}
|
||||
static value_type *get_by_name(class_type *klass, const char *name)
|
||||
{
|
||||
return g_flags_get_value_by_name(klass, name);
|
||||
}
|
||||
static value_type *get_by_nick(class_type *klass, const char *name)
|
||||
{
|
||||
return g_flags_get_value_by_nick(klass, name);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename EnumType, typename Traits>
|
||||
class EnumValue
|
||||
{
|
||||
typedef EnumValue self;
|
||||
|
||||
typedef typename Traits::value_type value_type;
|
||||
typedef typename Traits::class_type class_type;
|
||||
|
||||
value_type *value_;
|
||||
|
||||
static class_type *get_class()
|
||||
{
|
||||
auto c = Traits::get_class(get_type_());
|
||||
if (!c) {
|
||||
detail::try_throw(std::invalid_argument("unknown class"));
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
EnumValue(gint v) : EnumValue(static_cast<EnumType>(v)) {}
|
||||
EnumValue(value_type *_value) : value_(_value) {}
|
||||
|
||||
public:
|
||||
typedef EnumType enum_type;
|
||||
|
||||
static GType get_type_() { return traits::gtype<EnumType>::get_type(); }
|
||||
|
||||
EnumValue(EnumType v) : value_(Traits::get_value(get_class(), (int)v))
|
||||
{
|
||||
if (!value_)
|
||||
detail::try_throw(std::invalid_argument(
|
||||
"invalid value " + std::to_string((unsigned)v)));
|
||||
}
|
||||
|
||||
static self get_by_name(const gi::cstring_v name)
|
||||
{
|
||||
auto v = Traits::get_by_name(get_class(), name.c_str());
|
||||
if (!v)
|
||||
detail::try_throw(std::invalid_argument("unknown name"));
|
||||
else
|
||||
return self(v);
|
||||
}
|
||||
static self get_by_nick(const gi::cstring_v nick)
|
||||
{
|
||||
auto v = Traits::get_by_nick(get_class(), nick.c_str());
|
||||
if (!v)
|
||||
detail::try_throw(std::invalid_argument("unknown nick"));
|
||||
else
|
||||
return self(v);
|
||||
}
|
||||
|
||||
operator EnumType() const noexcept
|
||||
{
|
||||
return static_cast<EnumType>(value_->value);
|
||||
}
|
||||
|
||||
gi::cstring_v value_name() const noexcept { return value_->value_name; }
|
||||
|
||||
gi::cstring_v value_nick() const noexcept { return value_->value_nick; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// enumeration nick/name helpers
|
||||
template<typename EnumType>
|
||||
using EnumValue = detail::EnumValue<EnumType, detail::EnumValueTraits>;
|
||||
|
||||
template<typename EnumType,
|
||||
typename std::enable_if<traits::is_enumeration<EnumType>::value &&
|
||||
traits::gtype<EnumType>::value>::type * = nullptr>
|
||||
inline EnumValue<EnumType>
|
||||
value_info(EnumType v)
|
||||
{
|
||||
return EnumValue<EnumType>(v);
|
||||
}
|
||||
|
||||
// bitfield nick/name helpers
|
||||
template<typename EnumType>
|
||||
using FlagsValue = detail::EnumValue<EnumType, detail::FlagsValueTraits>;
|
||||
|
||||
template<typename EnumType,
|
||||
typename std::enable_if<traits::is_bitfield<EnumType>::value &&
|
||||
traits::gtype<EnumType>::value>::type * = nullptr>
|
||||
inline FlagsValue<EnumType>
|
||||
value_info(EnumType v)
|
||||
{
|
||||
return FlagsValue<EnumType>(v);
|
||||
}
|
||||
|
||||
// bitfield operation helpers
|
||||
template<typename FlagType,
|
||||
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
|
||||
nullptr>
|
||||
inline FlagType
|
||||
operator|(FlagType lhs, FlagType rhs)
|
||||
{
|
||||
return static_cast<FlagType>(
|
||||
static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs));
|
||||
}
|
||||
|
||||
template<typename FlagType,
|
||||
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
|
||||
nullptr>
|
||||
inline FlagType
|
||||
operator&(FlagType lhs, FlagType rhs)
|
||||
{
|
||||
return static_cast<FlagType>(
|
||||
static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs));
|
||||
}
|
||||
|
||||
template<typename FlagType,
|
||||
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
|
||||
nullptr>
|
||||
inline FlagType
|
||||
operator^(FlagType lhs, FlagType rhs)
|
||||
{
|
||||
return static_cast<FlagType>(
|
||||
static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs));
|
||||
}
|
||||
|
||||
template<typename FlagType,
|
||||
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
|
||||
nullptr>
|
||||
inline FlagType
|
||||
operator~(FlagType flags)
|
||||
{
|
||||
return static_cast<FlagType>(~static_cast<unsigned>(flags));
|
||||
}
|
||||
|
||||
template<typename FlagType,
|
||||
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
|
||||
nullptr>
|
||||
inline FlagType &
|
||||
operator|=(FlagType &lhs, FlagType rhs)
|
||||
{
|
||||
return (lhs = static_cast<FlagType>(
|
||||
static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs)));
|
||||
}
|
||||
|
||||
template<typename FlagType,
|
||||
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
|
||||
nullptr>
|
||||
inline FlagType &
|
||||
operator&=(FlagType &lhs, FlagType rhs)
|
||||
{
|
||||
return (lhs = static_cast<FlagType>(
|
||||
static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs)));
|
||||
}
|
||||
|
||||
template<typename FlagType,
|
||||
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
|
||||
nullptr>
|
||||
inline FlagType &
|
||||
operator^=(FlagType &lhs, FlagType rhs)
|
||||
{
|
||||
return (lhs = static_cast<FlagType>(
|
||||
static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs)));
|
||||
}
|
||||
|
||||
} // namespace gi
|
||||
|
||||
// sadly, the above templates are not picked up by ADL
|
||||
// and might therefore only end up used in case of a `using namespace gi`
|
||||
|
||||
// so also explicitly add operators in generated namespace
|
||||
// (by means of macro to simplify generation)
|
||||
|
||||
// see macro interface
|
||||
|
||||
/* NOTE:
|
||||
*
|
||||
* enumeration/bitfield TypeX is currently represented by an enum class TypeX
|
||||
* along with a namespace TypeXNS_ that holds the member functions.
|
||||
*
|
||||
* This could be merged into single class TypeX
|
||||
* (possibly using templated helpers);
|
||||
*
|
||||
* class TypeX
|
||||
* {
|
||||
* enum Enum {
|
||||
* VALUE_A,
|
||||
* VALUE_B
|
||||
* }
|
||||
* Enum value;
|
||||
*
|
||||
* (or C++17):
|
||||
* inline static const TypeX VALUE_A;
|
||||
* (note; constexpr not possible with incomplete type)
|
||||
*
|
||||
* constructor (Enum)
|
||||
* operator Enum()
|
||||
* ... etc ...
|
||||
* std::string value_name(); [opt]
|
||||
* std::string value_nick(); [opt]
|
||||
* }
|
||||
*
|
||||
* Disadvantage is that TypeX::VALUE_A would not have type TypeX,
|
||||
* although easily converted from/to TypeX, and so it would mostly work.
|
||||
* However, the operators above would have to be (even more) complicated and
|
||||
* accept various combinations of TypeX and TypeX::Enum (in case of a bitfield).
|
||||
*
|
||||
* All in all, the type mismatch (and moderately rare member function
|
||||
* occurrence) does not warrant going this way at this time. Also avoid C++17
|
||||
* for now, so as it stands ...
|
||||
*/
|
||||
|
||||
#endif // GI_ENUMFLAG_HPP
|
||||
170
cmake/external/glib/cppgir/gi/exception.hpp
vendored
Normal file
170
cmake/external/glib/cppgir/gi/exception.hpp
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
#ifndef GI_EXCEPTION_HPP
|
||||
#define GI_EXCEPTION_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
#include "wrap.hpp"
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
inline std::logic_error
|
||||
transform_error(GType tp, const char *name = nullptr)
|
||||
{
|
||||
auto n = g_type_name(tp);
|
||||
auto msg = std::string("could not transform value to type ") +
|
||||
detail::make_string(n);
|
||||
if (name)
|
||||
msg += std::string(" of property \'") + name + "\'";
|
||||
return std::invalid_argument(msg);
|
||||
}
|
||||
|
||||
inline std::logic_error
|
||||
unknown_property_error(GType tp, const gchar *property)
|
||||
{
|
||||
auto n = g_type_name(tp);
|
||||
auto msg = std::string("object of type ") + detail::make_string(n) +
|
||||
" does not have property \'" + detail::make_string(property) +
|
||||
"\'";
|
||||
return std::invalid_argument(msg);
|
||||
}
|
||||
|
||||
inline std::logic_error
|
||||
unknown_signal_error(GType tp, const std::string &name)
|
||||
{
|
||||
auto n = g_type_name(tp);
|
||||
auto msg = std::string("object of type ") + detail::make_string(n) +
|
||||
" does not have signal \'" + name + "\'";
|
||||
return std::invalid_argument(msg);
|
||||
}
|
||||
|
||||
inline std::logic_error
|
||||
invalid_signal_callback_error(
|
||||
GType tp, const std::string &name, const std::string &_msg)
|
||||
{
|
||||
auto n = g_type_name(tp);
|
||||
auto msg = std::string("invalid callback for signal ") + n + "::" + name +
|
||||
"; " + _msg;
|
||||
return std::invalid_argument(msg);
|
||||
}
|
||||
|
||||
// partially generated GError wrapper
|
||||
class Error : public gi::detail::GBoxedWrapperBase<Error, GError>
|
||||
{
|
||||
typedef gi::detail::GBoxedWrapperBase<Error, GError> super_type;
|
||||
|
||||
public:
|
||||
Error(GError *obj = nullptr) : super_type(obj) {}
|
||||
|
||||
static GType get_type_() G_GNUC_CONST { return g_error_get_type(); }
|
||||
|
||||
// use with care; dangling reference caution applies here
|
||||
gint &code_() { return gobj_()->code; }
|
||||
const gint &code_() const { return gobj_()->code; }
|
||||
|
||||
// use with care; dangling reference caution applies here
|
||||
gi::cstring_v message_() const { return gobj_()->message; }
|
||||
|
||||
// gboolean g_error_matches (const GError* error, GQuark domain, gint code);
|
||||
inline bool matches(GQuark domain, gint code) const
|
||||
{
|
||||
return g_error_matches(gobj_(), domain, code);
|
||||
}
|
||||
|
||||
}; // class
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace repository
|
||||
{
|
||||
namespace GLib
|
||||
{
|
||||
class Error_Ref;
|
||||
|
||||
class Error
|
||||
: public std::runtime_error,
|
||||
public detail::GBoxedWrapper<Error, ::GError, detail::Error, Error_Ref>
|
||||
{
|
||||
typedef std::runtime_error super;
|
||||
|
||||
static inline std::string make_message(GError *error)
|
||||
{
|
||||
return error ? detail::make_string(g_quark_to_string(error->domain)) +
|
||||
": " + detail::make_string(error->message) + "(" +
|
||||
std::to_string(error->code) + ")"
|
||||
: "";
|
||||
}
|
||||
|
||||
public:
|
||||
explicit Error(GError *obj = nullptr) : super(make_message(obj))
|
||||
{
|
||||
data_ = obj;
|
||||
}
|
||||
|
||||
// GError* g_error_new_literal (GQuark domain, gint code, const gchar*
|
||||
// message);
|
||||
static inline Error new_literal(
|
||||
GQuark domain, gint code, const std::string &message)
|
||||
{
|
||||
return Error(g_error_new_literal(
|
||||
domain, code, gi::unwrap(message, gi::transfer_none)));
|
||||
}
|
||||
|
||||
// GError* g_error_copy (const GError* error);
|
||||
inline Error copy() const { return Error(g_error_copy(gobj_())); }
|
||||
|
||||
// override wrap since we are no longer in a simple single-base case
|
||||
template<typename Cpp, typename Enable = typename std::enable_if<
|
||||
std::is_base_of<Error, Cpp>::value>::type>
|
||||
static Cpp wrap(const typename Cpp::BaseObjectType *obj)
|
||||
{
|
||||
static_assert(sizeof(Cpp) == sizeof(Error), "type wrap not supported");
|
||||
Error w(const_cast<GError *>(obj));
|
||||
return std::move(*static_cast<Cpp *>(&w));
|
||||
}
|
||||
};
|
||||
|
||||
class Error_Ref
|
||||
: public gi::detail::GBoxedRefWrapper<GLib::Error, ::GError, detail::Error>
|
||||
{
|
||||
typedef gi::detail::GBoxedRefWrapper<GLib::Error, ::GError, detail::Error>
|
||||
super_type;
|
||||
using super_type::super_type;
|
||||
|
||||
// GError* g_error_copy (const GError* error);
|
||||
inline Error copy() const { return Error(g_error_copy(gobj_())); }
|
||||
};
|
||||
|
||||
} // namespace GLib
|
||||
|
||||
template<>
|
||||
struct declare_cpptype_of<GError>
|
||||
{
|
||||
typedef GLib::Error type;
|
||||
};
|
||||
|
||||
} // namespace repository
|
||||
|
||||
inline void
|
||||
check_error(GError *error)
|
||||
{
|
||||
if (error)
|
||||
detail::try_throw(repository::GLib::Error(error));
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
inline repository::GLib::Error
|
||||
missing_symbol_error(const std::string &symbol)
|
||||
{
|
||||
::GQuark domain = g_quark_from_static_string("gi-error-quark");
|
||||
auto error =
|
||||
g_error_new(domain, 0, "could not find symbol %s", symbol.c_str());
|
||||
return repository::GLib::Error(error);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
} // namespace gi
|
||||
|
||||
#endif // GI_EXCEPTION_HPP
|
||||
112
cmake/external/glib/cppgir/gi/expected.hpp
vendored
Normal file
112
cmake/external/glib/cppgir/gi/expected.hpp
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
#ifndef GI_EXPECTED_HPP
|
||||
#define GI_EXPECTED_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
#include "exception.hpp"
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
// alias so we might route to a std type some day ...
|
||||
template<typename T, typename E>
|
||||
#ifdef expected_lite_VERSION
|
||||
using expected = nonstd::expected<T, E>;
|
||||
#else
|
||||
using expected = std::expected<T, E>;
|
||||
#endif
|
||||
|
||||
template<typename E>
|
||||
#ifdef expected_lite_VERSION
|
||||
using unexpected = nonstd::unexpected_type<E>;
|
||||
#else
|
||||
using unexpected = std::unexpected<E>;
|
||||
#endif
|
||||
|
||||
// standardize on glib error
|
||||
template<typename T>
|
||||
using result = expected<T, repository::GLib::Error>;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// only use nonstd if it does not delegate to std (in incomplete way)
|
||||
#if defined(expected_lite_VERSION) && !nsel_USES_STD_EXPECTED
|
||||
inline unexpected<repository::GLib::Error>
|
||||
make_unexpected(repository::GLib::Error error)
|
||||
{
|
||||
assert(error);
|
||||
return nonstd::make_unexpected(std::move(error));
|
||||
}
|
||||
#else
|
||||
inline unexpected<repository::GLib::Error>
|
||||
make_unexpected(repository::GLib::Error error)
|
||||
{
|
||||
assert(error);
|
||||
return std::unexpected<repository::GLib::Error>(std::move(error));
|
||||
}
|
||||
#endif
|
||||
|
||||
inline unexpected<repository::GLib::Error>
|
||||
make_unexpected(GError *error)
|
||||
{
|
||||
assert(error);
|
||||
return make_unexpected(repository::GLib::Error(error));
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// no forwarding reference; T must be non-reference type
|
||||
template<typename T>
|
||||
result<T>
|
||||
make_result(T t, GError *error)
|
||||
{
|
||||
if (error)
|
||||
return detail::make_unexpected(error);
|
||||
return t;
|
||||
}
|
||||
|
||||
// rough helpers to unwrap result/expected
|
||||
// unwrap by move
|
||||
template<typename T>
|
||||
T
|
||||
expect(gi::result<T> &&t)
|
||||
{
|
||||
if (!t) {
|
||||
g_critical("error result %s", t.error().what());
|
||||
detail::try_throw(std::move(t.error()));
|
||||
}
|
||||
return std::move(*t);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename T>
|
||||
void test_result(const gi::result<T> &);
|
||||
template<typename T>
|
||||
int test_result(const T &);
|
||||
template<typename T>
|
||||
using is_result = std::is_same<void,
|
||||
decltype(test_result(std::forward<T>(std::declval<T>())))>;
|
||||
} // namespace detail
|
||||
|
||||
// should only be used for a non-result
|
||||
// (e.g. avoid l-value result ending up here)
|
||||
template<typename T, typename Enable = typename std::enable_if<
|
||||
!detail::is_result<T>::value>::type>
|
||||
T
|
||||
expect(T &&t)
|
||||
{
|
||||
return std::forward<T>(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct rv
|
||||
{
|
||||
#if GI_DL && GI_EXPECTED
|
||||
using type = gi::result<T>;
|
||||
#else
|
||||
using type = T;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace gi
|
||||
|
||||
#endif // GI_EXPECTED_HPP
|
||||
20
cmake/external/glib/cppgir/gi/gi.hpp
vendored
Normal file
20
cmake/external/glib/cppgir/gi/gi.hpp
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef GI_HPP
|
||||
#define GI_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
#include "container.hpp"
|
||||
#include "enumflag.hpp"
|
||||
#include "exception.hpp"
|
||||
#include "expected.hpp"
|
||||
#include "object.hpp"
|
||||
#include "objectclass.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
// check that include path has been setup properly to include override
|
||||
#if defined(__has_include)
|
||||
#if !__has_include(<glib/glib_extra_def.hpp>)
|
||||
#warning "overrides not found in include path"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // GI_HPP
|
||||
283
cmake/external/glib/cppgir/gi/gi_inc.hpp
vendored
Normal file
283
cmake/external/glib/cppgir/gi/gi_inc.hpp
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
#ifndef GI_INC_HPP
|
||||
#define GI_INC_HPP
|
||||
|
||||
// gi preprocessor/macro interface
|
||||
// aka global module fragment part
|
||||
|
||||
#define GI_VERSION_MAJAOR (2)
|
||||
#define GI_VERSION_MINOR (0)
|
||||
#define GI_VERSION_MICRO (0)
|
||||
|
||||
#ifdef GI_INLINE
|
||||
#define GI_INLINE_DECL inline
|
||||
#else
|
||||
#define GI_INLINE_DECL
|
||||
#endif
|
||||
|
||||
// == we could be included in some module setting
|
||||
#ifdef GI_MODULE_IN_INTERFACE
|
||||
#if __cplusplus < 201703L
|
||||
#error "need at least C++17 for modules"
|
||||
#endif
|
||||
#define GI_MODULE_EXPORT export
|
||||
#define GI_MODULE_INLINE inline
|
||||
#define GI_MODULE_STATIC_OR_INLINE inline
|
||||
#else // GI_MODULE_IN_INTERFACE
|
||||
#define GI_MODULE_EXPORT
|
||||
#define GI_MODULE_INLINE
|
||||
#define GI_MODULE_STATIC_OR_INLINE static
|
||||
#endif
|
||||
|
||||
// only used in module context
|
||||
#ifdef GI_MODULE_EXTERN
|
||||
#define GI_MODULE_EXTERN_BEGIN extern "C++" {
|
||||
#define GI_MODULE_EXTERN_END }
|
||||
#else
|
||||
#define GI_MODULE_EXTERN_BEGIN
|
||||
#define GI_MODULE_EXTERN_END
|
||||
#endif
|
||||
// related; another clang warning
|
||||
// https://github.com/llvm/llvm-project/issues/68615
|
||||
// `#include <filename>' attaches the declarations to the named module`
|
||||
#ifdef __clang__
|
||||
#define GI_MODULE_DISABLE_WARN_BEGIN \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Winclude-angled-in-module-purview\"")
|
||||
#define GI_MODULE_DISABLE_WARN_END _Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
#define GI_MODULE_DISABLE_WARN_BEGIN
|
||||
#define GI_MODULE_DISABLE_WARN_END
|
||||
#endif
|
||||
|
||||
#define GI_MODULE_BEGIN \
|
||||
GI_MODULE_DISABLE_WARN_BEGIN \
|
||||
GI_MODULE_EXTERN_BEGIN
|
||||
|
||||
#define GI_MODULE_END \
|
||||
GI_MODULE_EXTERN_END \
|
||||
GI_MODULE_DISABLE_WARN_BEGIN
|
||||
// ==
|
||||
|
||||
// lots of declarations might be attributed as deprecated,
|
||||
// but not so annotated, so let's avoid warning floods
|
||||
// also handle complaints about const qualified casts
|
||||
// (due to silly const qualified scalar parameters)
|
||||
#define GI_DISABLE_DEPRECATED_WARN_BEGIN \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wignored-qualifiers\"")
|
||||
|
||||
#define GI_DISABLE_DEPRECATED_WARN_END \
|
||||
_Pragma("GCC diagnostic pop") _Pragma("GCC diagnostic pop")
|
||||
|
||||
// typically clang might warn but gcc might complain about pragma clang ...
|
||||
#ifdef GI_CLASS_IMPL_PRAGMA
|
||||
#ifndef GI_CLASS_IMPL_BEGIN
|
||||
#define GI_CLASS_IMPL_BEGIN \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Woverloaded-virtual\"")
|
||||
#endif
|
||||
|
||||
#ifndef GI_CLASS_IMPL_END
|
||||
#define GI_CLASS_IMPL_END _Pragma("GCC diagnostic pop")
|
||||
#endif
|
||||
#else
|
||||
#define GI_CLASS_IMPL_BEGIN
|
||||
#define GI_CLASS_IMPL_END
|
||||
#endif
|
||||
|
||||
// attempt to auto-discover exception support:
|
||||
#ifndef GI_CONFIG_EXCEPTIONS
|
||||
#if defined(_MSC_VER)
|
||||
#include <cstddef> // for _HAS_EXCEPTIONS
|
||||
#endif
|
||||
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
|
||||
#define GI_CONFIG_EXCEPTIONS 1
|
||||
#else
|
||||
#define GI_CONFIG_EXCEPTIONS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#if __has_include("nonstd/expected.hpp")
|
||||
#if __cpp_concepts >= 202002L
|
||||
// this is also required in gcc's libstdc++ expected
|
||||
#else
|
||||
// so if that is missing, then prevent delegation to <expected>
|
||||
#define nsel_CONFIG_SELECT_EXPECTED nsel_EXPECTED_NONSTD
|
||||
#endif
|
||||
#include "nonstd/expected.hpp"
|
||||
#else
|
||||
#include <expected>
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
// define << operator if not unwanted
|
||||
#ifndef GI_NO_STRING_IOS
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
#if GI_DL
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
#include <gobject/gvaluecollector.h>
|
||||
|
||||
// various macros from all over the place
|
||||
// sadly, module refactor means breaking things apart
|
||||
|
||||
// exception
|
||||
// exception specification is generated according to settings and situation
|
||||
// some derived code (e.g. overrides) may need to follow suit accordingly
|
||||
#if GI_EXPECTED
|
||||
// no exception if reported through expected
|
||||
#define GI_NOEXCEPT_DECL(nonthrowing) noexcept
|
||||
#elif GI_DL
|
||||
// otherwise, everything can start failing if resolved at runtime
|
||||
#define GI_NOEXCEPT_DECL(nonthrowing)
|
||||
#else
|
||||
// otherwise, depends on whether (wrapped) function is (GError) throwing
|
||||
#define GI_NOEXCEPT_DECL(nonthrowing) noexcept(nonthrowing)
|
||||
#endif
|
||||
|
||||
// callback
|
||||
#define GI_CB_ARG_CALLBACK_CUSTOM(Type, CF_CType, CF_handler) \
|
||||
struct Type \
|
||||
{ \
|
||||
using handler_cb_type = CF_CType; \
|
||||
static constexpr auto handler = CF_handler; \
|
||||
}
|
||||
|
||||
// boxed
|
||||
// should be used within gi.repository namespace
|
||||
#define GI_ENABLE_BOXED_COPY(CType) \
|
||||
template<> \
|
||||
struct enable_boxed_copy<CType> : public std::true_type \
|
||||
{};
|
||||
|
||||
// enumflag
|
||||
#define GI_FLAG_OPERATORS(FlagType) \
|
||||
inline FlagType operator|(FlagType lhs, FlagType rhs) \
|
||||
{ \
|
||||
return static_cast<FlagType>( \
|
||||
static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs)); \
|
||||
} \
|
||||
\
|
||||
inline FlagType operator&(FlagType lhs, FlagType rhs) \
|
||||
{ \
|
||||
return static_cast<FlagType>( \
|
||||
static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs)); \
|
||||
} \
|
||||
\
|
||||
inline FlagType operator^(FlagType lhs, FlagType rhs) \
|
||||
{ \
|
||||
return static_cast<FlagType>( \
|
||||
static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs)); \
|
||||
} \
|
||||
\
|
||||
inline FlagType operator~(FlagType flags) \
|
||||
{ \
|
||||
return static_cast<FlagType>(~static_cast<unsigned>(flags)); \
|
||||
} \
|
||||
\
|
||||
inline FlagType &operator|=(FlagType &lhs, FlagType rhs) \
|
||||
{ \
|
||||
return (lhs = static_cast<FlagType>( \
|
||||
static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs))); \
|
||||
} \
|
||||
\
|
||||
inline FlagType &operator&=(FlagType &lhs, FlagType rhs) \
|
||||
{ \
|
||||
return (lhs = static_cast<FlagType>( \
|
||||
static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs))); \
|
||||
} \
|
||||
\
|
||||
inline FlagType &operator^=(FlagType &lhs, FlagType rhs) \
|
||||
{ \
|
||||
return (lhs = static_cast<FlagType>( \
|
||||
static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs))); \
|
||||
}
|
||||
|
||||
#define GI_ENUM_NUMERIC(EnumType) \
|
||||
inline std::underlying_type<EnumType>::type operator+(EnumType e) \
|
||||
{ \
|
||||
return static_cast<std::underlying_type<EnumType>::type>(e); \
|
||||
}
|
||||
|
||||
// objectclass
|
||||
// helper macro to obtain data from factory (if provided)
|
||||
#define GI_MEMBER_INIT_DATA(ClassType, factory) \
|
||||
(factory ? ((ClassType(*)())(factory))() : ClassType());
|
||||
|
||||
// conflicts might arise between interfaces and/or class
|
||||
// generate some dummy check types to force failure
|
||||
#define GI_MEMBER_CHECK_CONFLICT(member) _check_member_conflict_##member
|
||||
|
||||
// generated code tries to detect a defined member in SubClass as follows
|
||||
#define GI_MEMBER_DEFAULT_HAS_DEFINITION(BaseDef, member) \
|
||||
template<typename SubClass> \
|
||||
constexpr static bool has_definition(const member##_t *, const SubClass *) \
|
||||
{ \
|
||||
/* the use of conflict type check is only to trigger a compiler error \
|
||||
* if there is such a conflict \
|
||||
* in that case, manual specification of definitions are needed \
|
||||
* (which will then avoid this code path instantiation) \
|
||||
* (type should never be void, so merely serves as dummy check) \
|
||||
*/ \
|
||||
return std::is_void<typename SubClass::GI_MEMBER_CHECK_CONFLICT( \
|
||||
member)>::value || \
|
||||
!std::is_same<decltype(&BaseDef::member##_), \
|
||||
decltype(&SubClass::member##_)>::value; \
|
||||
}
|
||||
|
||||
// helper macro used in generated code
|
||||
#define GI_MEMBER_DEFINE(BaseDef, member) \
|
||||
struct member##_tag; \
|
||||
using member##_t = detail::member_type<member##_tag>; \
|
||||
member##_t member; \
|
||||
GI_MEMBER_DEFAULT_HAS_DEFINITION(BaseDef, member)
|
||||
|
||||
// the automated way might/will fail in case of overload resolution failure
|
||||
// (due to member conflicts with interfaces)
|
||||
// so the following can be used to specify definition situation
|
||||
// should be used in an inner struct DefinitionData in the SubClass
|
||||
#define GI_DEFINES_MEMBER(BaseDef, member, _defines) \
|
||||
template<typename SubClass> \
|
||||
constexpr static bool defines( \
|
||||
const BaseDef::TypeInitData::member##_t *, const SubClass *) \
|
||||
{ \
|
||||
return _defines; \
|
||||
}
|
||||
|
||||
// uses function overload on all of the above to determine
|
||||
// if member of DefData is defined/overridden in SubClass
|
||||
// (and should then be registered in the class/interface struct)
|
||||
#define GI_MEMBER_HAS_DEFINITION(SubClass, DefData, member) \
|
||||
DefData::defines((member##_t *)(nullptr), (SubClass *)(nullptr))
|
||||
|
||||
#endif // GI_INC_HPP
|
||||
694
cmake/external/glib/cppgir/gi/object.hpp
vendored
Normal file
694
cmake/external/glib/cppgir/gi/object.hpp
vendored
Normal file
@@ -0,0 +1,694 @@
|
||||
#ifndef GI_OBJECT_HPP
|
||||
#define GI_OBJECT_HPP
|
||||
|
||||
#include "callback.hpp"
|
||||
#include "container.hpp"
|
||||
#include "exception.hpp"
|
||||
#include "objectbase.hpp"
|
||||
#include "paramspec.hpp"
|
||||
#include "value.hpp"
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
// helper
|
||||
|
||||
// signal argument connect/emit helper;
|
||||
// turn (C++) argument into a GType or GValue
|
||||
// most arguments are inputs (with specific GType),
|
||||
// but some arguments are used as output with G_TYPE_POINTER (e.g. int*)
|
||||
// which are mapped to (e.g.) int* or int& in C++ signature
|
||||
template<typename Arg, bool DECAY>
|
||||
struct signal_arg
|
||||
{
|
||||
static GType get_type() { return traits::gtype<Arg>::get_type(); }
|
||||
static detail::Value make(Arg arg)
|
||||
{
|
||||
return detail::Value(std::forward<Arg>(arg));
|
||||
}
|
||||
};
|
||||
|
||||
// re-route e.g. const std::string& cases
|
||||
template<typename Arg>
|
||||
struct signal_arg<const Arg &, false> : public signal_arg<const Arg &, true>
|
||||
{};
|
||||
|
||||
template<typename Arg>
|
||||
struct signal_arg<Arg &, false>
|
||||
{
|
||||
static GType get_type() { return G_TYPE_POINTER; }
|
||||
static detail::Value make(Arg &arg)
|
||||
{
|
||||
return signal_arg<Arg *, false>::make(&arg);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Arg>
|
||||
struct signal_arg<Arg *, false>
|
||||
{
|
||||
static GType get_type() { return G_TYPE_POINTER; }
|
||||
static detail::Value make(Arg *arg)
|
||||
{
|
||||
// (size of) wrapper argument should match wrappee
|
||||
static_assert(sizeof(typename traits::ctype<Arg>::type) == sizeof(Arg), "");
|
||||
// the above should suffice for proper handling
|
||||
// however, in these output cases, transfer should also be considered,
|
||||
// which is not (yet) available here
|
||||
// (but could be passed along similar to callback argument info)
|
||||
// so, restrict to plain cases for now
|
||||
static_assert(traits::is_plain<Arg>::value, "");
|
||||
return detail::Value(gpointer(arg));
|
||||
}
|
||||
};
|
||||
|
||||
// returns -size if signed numeric, +size if unsigned numeric, otherwise 0
|
||||
inline int
|
||||
get_number_size_signed(GType type)
|
||||
{
|
||||
// note; these are generally lower (absolute) bounds
|
||||
// at least it works in the context where it is used below
|
||||
#define GI_HANDLE_TYPE_SWITCH(cpptype, g_type, factor) \
|
||||
case g_type: \
|
||||
return factor * int(sizeof(cpptype));
|
||||
switch (type) {
|
||||
GI_HANDLE_TYPE_SWITCH(gchar, G_TYPE_CHAR, -1)
|
||||
GI_HANDLE_TYPE_SWITCH(guchar, G_TYPE_UCHAR, 1)
|
||||
GI_HANDLE_TYPE_SWITCH(gint, G_TYPE_INT, -1)
|
||||
GI_HANDLE_TYPE_SWITCH(guint, G_TYPE_UINT, 1)
|
||||
GI_HANDLE_TYPE_SWITCH(glong, G_TYPE_LONG, -1)
|
||||
GI_HANDLE_TYPE_SWITCH(gulong, G_TYPE_ULONG, 1)
|
||||
GI_HANDLE_TYPE_SWITCH(gint64, G_TYPE_INT64, -1)
|
||||
GI_HANDLE_TYPE_SWITCH(guint64, G_TYPE_UINT64, 1)
|
||||
}
|
||||
#undef GI_HANDLE_TYPE_SWITCH
|
||||
return 0;
|
||||
}
|
||||
|
||||
// glib type systems treats G_TYPE_INT64 as distinct from the other types
|
||||
// in practice, however, quite likely C gint64 == long
|
||||
inline bool
|
||||
compatible_type(GType expected, GType actual)
|
||||
{
|
||||
if (expected == G_TYPE_BOOLEAN)
|
||||
return std::abs(get_number_size_signed(actual)) == sizeof(gboolean);
|
||||
auto ssa_e = get_number_size_signed(expected);
|
||||
auto ssa_a = get_number_size_signed(actual);
|
||||
return ssa_e == ssa_a;
|
||||
}
|
||||
|
||||
inline void
|
||||
check_signal_type(GType tp, const gi::cstring_v name, GType return_type,
|
||||
GType *param_types, guint n_params)
|
||||
{
|
||||
const char *errmsg("expected ");
|
||||
auto check_types = [tp, &name, &errmsg](const std::string &desc,
|
||||
GType expected, GType actual) {
|
||||
// normalize
|
||||
expected &= ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
actual &= ~G_SIGNAL_TYPE_STATIC_SCOPE;
|
||||
if (expected == actual || compatible_type(expected, actual) ||
|
||||
g_type_is_a(expected, actual))
|
||||
return;
|
||||
std::string msg = errmsg;
|
||||
msg += desc + " type ";
|
||||
msg += detail::make_string(g_type_name(expected)) + " != ";
|
||||
msg += detail::make_string(g_type_name(actual));
|
||||
detail::try_throw(invalid_signal_callback_error(tp, name, msg));
|
||||
};
|
||||
|
||||
// determine signal (detail)
|
||||
guint id;
|
||||
GQuark detail;
|
||||
if (!g_signal_parse_name(name.c_str(), tp, &id, &detail, false) || (id == 0))
|
||||
detail::try_throw(unknown_signal_error(tp, name));
|
||||
// get signal info
|
||||
GSignalQuery query;
|
||||
g_signal_query(id, &query);
|
||||
// check
|
||||
if (n_params != query.n_params + 1) {
|
||||
auto msg = std::string(errmsg) + "argument count ";
|
||||
msg += std::to_string(query.n_params);
|
||||
msg += " != " + std::to_string(n_params);
|
||||
detail::try_throw(invalid_signal_callback_error(tp, name, msg));
|
||||
}
|
||||
check_types("return", query.return_type, return_type);
|
||||
check_types("instance", query.itype, param_types[0]);
|
||||
const std::string arg("argument ");
|
||||
for (guint i = 0; i < query.n_params; ++i)
|
||||
check_types(
|
||||
arg + std::to_string(i + 1), query.param_types[i], param_types[i + 1]);
|
||||
}
|
||||
|
||||
template<typename G>
|
||||
struct signal_type;
|
||||
|
||||
template<typename R, typename... Args>
|
||||
struct signal_type<R(Args...)>
|
||||
{
|
||||
static void check(GType tp, const gi::cstring_v name)
|
||||
{
|
||||
// capture type info and delegate
|
||||
const int argcount = sizeof...(Args);
|
||||
GType ti[] = {signal_arg<Args, false>::get_type()...};
|
||||
check_signal_type(tp, name, traits::gtype<R>::get_type(), ti, argcount);
|
||||
}
|
||||
};
|
||||
|
||||
// like GParameter, but with extra Value trimming
|
||||
struct Parameter
|
||||
{
|
||||
const char *name;
|
||||
detail::Value value;
|
||||
};
|
||||
|
||||
#ifdef GI_OBJECT_NEWV
|
||||
GI_DISABLE_DEPRECATED_WARN_BEGIN
|
||||
static_assert(sizeof(Parameter) == sizeof(GParameter), "");
|
||||
GI_DISABLE_DEPRECATED_WARN_END
|
||||
#endif
|
||||
|
||||
inline void
|
||||
fill_parameters(Parameter *)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
template<typename Arg, typename... Args>
|
||||
inline void
|
||||
fill_parameters(Parameter *param, const char *name, Arg &&arg, Args &&...args)
|
||||
{
|
||||
param->name = name;
|
||||
param->value.init<typename std::remove_reference<Arg>::type>();
|
||||
set_value(¶m->value, std::forward<Arg>(arg));
|
||||
fill_parameters(param + 1, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#if GLIB_CHECK_VERSION(2, 54, 0)
|
||||
#define GI_GOBJECT_PROPERTY_VALUE 1
|
||||
#endif
|
||||
|
||||
namespace repository
|
||||
{
|
||||
/* if you have arrived here due to an ambiguous GObject reference
|
||||
* (both the C typedef GObject and this namespace)
|
||||
* then that can be worked-around by:
|
||||
* + using _GObject (struct name instead)
|
||||
* + adjust 'using namespace' style imports e.g. alias
|
||||
* namespace GObject_ = gi::GObject;
|
||||
* or simply do not mention GObject at all and simply use the wrappers ;-)
|
||||
*/
|
||||
namespace GObject
|
||||
{
|
||||
typedef std::vector<detail::Parameter> construct_params;
|
||||
|
||||
template<typename... Args>
|
||||
construct_params
|
||||
make_construct_params(Args &&...args)
|
||||
{
|
||||
const int nparams = sizeof...(Args) / 2;
|
||||
construct_params parameters;
|
||||
parameters.resize(nparams);
|
||||
detail::fill_parameters(parameters.data(), std::forward<Args>(args)...);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
class Object : public detail::ObjectBase
|
||||
{
|
||||
typedef Object self;
|
||||
typedef detail::ObjectBase super_type;
|
||||
|
||||
public:
|
||||
typedef ::GObject BaseObjectType;
|
||||
|
||||
Object(std::nullptr_t = nullptr) : super_type() {}
|
||||
|
||||
BaseObjectType *gobj_() { return (BaseObjectType *)super_type::gobj_(); }
|
||||
const BaseObjectType *gobj_() const
|
||||
{
|
||||
return (const BaseObjectType *)super_type::gobj_();
|
||||
}
|
||||
BaseObjectType *gobj_copy_() const
|
||||
{
|
||||
return (BaseObjectType *)super_type::gobj_copy_();
|
||||
}
|
||||
|
||||
// class type
|
||||
static GType get_type_() { return G_TYPE_OBJECT; }
|
||||
// instance type
|
||||
GType gobj_type_() const { return G_OBJECT_TYPE(gobj_()); }
|
||||
|
||||
// type-erased generic object creation
|
||||
// transfer full return
|
||||
static gpointer new_(GType gtype, const construct_params ¶ms)
|
||||
{
|
||||
#ifdef GI_OBJECT_NEWV
|
||||
GI_DISABLE_DEPRECATED_WARN_BEGIN
|
||||
auto result =
|
||||
g_object_newv(gtype, params.size(), (GParameter *)params.data());
|
||||
GI_DISABLE_DEPRECATED_WARN_END
|
||||
#else
|
||||
std::vector<const char *> names;
|
||||
std::vector<GValue> values;
|
||||
names.reserve(params.size());
|
||||
values.reserve(params.size());
|
||||
// ownership remains in params
|
||||
for (auto &&p : params) {
|
||||
names.push_back(p.name);
|
||||
values.emplace_back(p.value);
|
||||
}
|
||||
auto result = g_object_new_with_properties(
|
||||
gtype, params.size(), names.data(), values.data());
|
||||
#endif
|
||||
// GIR says transfer full, but let's be careful and really make it so
|
||||
// if likely still floating, then we assume ownership
|
||||
// but if it is no longer, then it has already been stolen (e.g.
|
||||
// GtkWindow), and we need to add one here
|
||||
if (g_type_is_a(gtype, G_TYPE_INITIALLY_UNOWNED))
|
||||
g_object_ref_sink(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// type-based generic object creation
|
||||
template<typename CTYPE, typename... Args>
|
||||
static auto new_(GType gtype, Args &&...args)
|
||||
{
|
||||
auto parameters = make_construct_params(std::forward<Args>(args)...);
|
||||
auto *result = CTYPE(new_(gtype, parameters));
|
||||
return gi::wrap(result, transfer_full);
|
||||
}
|
||||
|
||||
// type-based generic object creation
|
||||
// Args are a sequence of name, value
|
||||
template<typename TYPE, typename... Args>
|
||||
static TYPE new_(Args &&...args)
|
||||
{
|
||||
return new_<typename TYPE::BaseObjectType *>(
|
||||
TYPE::get_type_(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// property stuff
|
||||
// generic type unsafe
|
||||
template<typename V>
|
||||
self &set_property(ParamSpec _pspec, V &&val)
|
||||
{
|
||||
// additional checks
|
||||
// allows for basic conversion between arithmetic types
|
||||
// without worrying about those details
|
||||
auto pspec = _pspec.gobj_();
|
||||
detail::Value v(std::forward<V>(val));
|
||||
detail::Value dest;
|
||||
GValue *p = &v;
|
||||
if (G_VALUE_TYPE(&v) != pspec->value_type) {
|
||||
g_value_init(&dest, pspec->value_type);
|
||||
if (!g_value_transform(&v, &dest))
|
||||
detail::try_throw(
|
||||
detail::transform_error(pspec->value_type, pspec->name));
|
||||
p = &dest;
|
||||
}
|
||||
g_object_set_property(gobj_(), pspec->name, p);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
self &set_property(const gi::cstring_v propname, V &&val)
|
||||
{
|
||||
return set_property<V>(find_property(propname, true), std::forward<V>(val));
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
self &set_properties(const gi::cstring_v propname, V &&val)
|
||||
{
|
||||
return set_property<V>(propname, std::forward<V>(val));
|
||||
}
|
||||
|
||||
// set a number of props
|
||||
template<typename V, typename... Args>
|
||||
self &set_properties(const gi::cstring_v propname, V &&val, Args... args)
|
||||
{
|
||||
g_object_freeze_notify(gobj_());
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
set_property(propname, std::forward<V>(val));
|
||||
set_properties(std::forward<Args>(args)...);
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
} catch (...) {
|
||||
g_object_thaw_notify(gobj_());
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
g_object_thaw_notify(gobj_());
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef GI_GOBJECT_PROPERTY_VALUE
|
||||
self &set_property(const gi::cstring_v propname, Value val)
|
||||
{
|
||||
g_object_set_property(gobj_(), propname.c_str(), val.gobj_());
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename V>
|
||||
V get_property(const char *propname) const
|
||||
{
|
||||
// this would return a ref to what is owned by stack-local v below
|
||||
static_assert(!traits::is_reftype<V>::value, "dangling ref");
|
||||
detail::Value v;
|
||||
v.init<V>();
|
||||
// the _get_ already tries to transform
|
||||
// also close enough to const
|
||||
g_object_get_property(const_cast<::GObject *>(gobj_()), propname, &v);
|
||||
return detail::get_value<V>(&v);
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
V get_property(const gi::cstring_v propname) const
|
||||
{
|
||||
return get_property<V>(propname.c_str());
|
||||
}
|
||||
|
||||
#ifdef GI_GOBJECT_PROPERTY_VALUE
|
||||
Value get_property(const gi::cstring_v propname) const
|
||||
{
|
||||
Value result;
|
||||
const gchar *name = propname.c_str();
|
||||
GValue *val = result.gobj_();
|
||||
g_object_getv(const_cast<::GObject *>(gobj_()), 1, &name, val);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ParamSpec find_property(
|
||||
GType gtype, const gi::cstring_v propname, bool _throw = false)
|
||||
{
|
||||
GParamSpec *spec;
|
||||
if (g_type_is_a(gtype, G_TYPE_INTERFACE)) {
|
||||
// interface should be loaded if we have an instance here
|
||||
auto vtable = g_type_default_interface_peek(gtype);
|
||||
spec = g_object_interface_find_property(vtable, propname.c_str());
|
||||
} else {
|
||||
spec = g_object_class_find_property(
|
||||
(GObjectClass *)g_type_class_peek(gtype), propname.c_str());
|
||||
}
|
||||
if (_throw && !spec)
|
||||
detail::try_throw(
|
||||
detail::unknown_property_error(gtype, propname.c_str()));
|
||||
return gi::wrap(spec, transfer_none);
|
||||
}
|
||||
|
||||
ParamSpec find_property(
|
||||
const gi::cstring_v propname, bool _throw = false) const
|
||||
{
|
||||
return find_property(gobj_type_(), propname, _throw);
|
||||
}
|
||||
|
||||
gi::Collection<gi::DSpan, GParamSpec *, gi::transfer_container_t>
|
||||
list_properties() const
|
||||
{
|
||||
GParamSpec **specs;
|
||||
guint nspecs = 0;
|
||||
if (g_type_is_a(gobj_type_(), G_TYPE_INTERFACE)) {
|
||||
// interface should be loaded if we have an instance here
|
||||
auto vtable = g_type_default_interface_peek(gobj_type_());
|
||||
specs = g_object_interface_list_properties(vtable, &nspecs);
|
||||
} else {
|
||||
specs =
|
||||
g_object_class_list_properties(G_OBJECT_GET_CLASS(gobj_()), &nspecs);
|
||||
}
|
||||
return wrap_to<
|
||||
gi::Collection<gi::DSpan, GParamSpec *, gi::transfer_container_t>>(
|
||||
specs, nspecs, transfer_container);
|
||||
}
|
||||
|
||||
// signal stuff
|
||||
private:
|
||||
template<typename F, typename Functor>
|
||||
gulong connect_data(
|
||||
const gi::cstring_v signal, Functor &&f, GConnectFlags flags)
|
||||
{
|
||||
// runtime signature check
|
||||
detail::signal_type<F>::check(gobj_type_(), signal);
|
||||
auto w = new detail::transform_signal_wrapper<F>(std::forward<Functor>(f));
|
||||
// mind gcc's -Wcast-function-type
|
||||
return g_signal_connect_data(gobj_(), signal.c_str(),
|
||||
(GCallback)&w->wrapper, w, (GClosureNotify)(GCallback)&w->destroy,
|
||||
flags);
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename F, typename Functor>
|
||||
gulong connect(const gi::cstring_v signal, Functor &&f)
|
||||
{
|
||||
return connect_data<F, Functor>(
|
||||
signal, std::forward<Functor>(f), (GConnectFlags)0);
|
||||
}
|
||||
|
||||
template<typename F, typename Functor>
|
||||
gulong connect_after(const gi::cstring_v signal, Functor &&f)
|
||||
{
|
||||
return connect_data<F, Functor>(
|
||||
signal, std::forward<Functor>(f), G_CONNECT_AFTER);
|
||||
}
|
||||
|
||||
// TODO the object variants ??
|
||||
|
||||
// in case of unsupported signal signature
|
||||
// connect using a plain C signature without check/transform (wrap/unwrap)
|
||||
template<typename F, typename Functor>
|
||||
gulong connect_unchecked(
|
||||
const gi::cstring_v signal, Functor &&f, GConnectFlags flags = {})
|
||||
{
|
||||
auto w = new detail::callback_wrapper<F>(std::forward<Functor>(f));
|
||||
// mind gcc's -Wcast-function-type
|
||||
return g_signal_connect_data(gobj_(), signal.c_str(),
|
||||
(GCallback)&w->wrapper, w, (GClosureNotify)(GCallback)&w->destroy,
|
||||
flags);
|
||||
}
|
||||
|
||||
void disconnect(gulong id) { g_signal_handler_disconnect(gobj_(), id); }
|
||||
|
||||
// Args... may be explicitly specified or deduced
|
||||
// if deduced; arrange to decay/strip reference below
|
||||
// if not deduced; may need to considere specified type as-is
|
||||
template<typename R, bool DECAY = true, typename... Args>
|
||||
R emit(const gi::cstring_v signal, Args &&...args)
|
||||
{
|
||||
// static constexpr bool DECAY = true;
|
||||
guint id;
|
||||
GQuark detail;
|
||||
if (!g_signal_parse_name(signal.c_str(), gobj_type_(), &id, &detail, true))
|
||||
detail::try_throw(std::out_of_range(std::string("unknown signal name: ") +
|
||||
detail::make_string(signal.c_str())));
|
||||
|
||||
detail::Value values[] = {detail::Value(*this),
|
||||
detail::signal_arg<Args, DECAY>::make(std::forward<Args>(args))...};
|
||||
detail::Value retv;
|
||||
retv.init<R>();
|
||||
g_signal_emitv(values, id, detail, &retv);
|
||||
return detail::get_value<R>(&retv);
|
||||
}
|
||||
|
||||
void handler_block(gulong handler_id)
|
||||
{
|
||||
g_signal_handler_block(gobj_(), handler_id);
|
||||
}
|
||||
|
||||
void handler_unblock(gulong handler_id)
|
||||
{
|
||||
g_signal_handler_unblock(gobj_(), handler_id);
|
||||
}
|
||||
|
||||
bool handler_is_connected(gulong handler_id)
|
||||
{
|
||||
return g_signal_handler_is_connected(gobj_(), handler_id);
|
||||
}
|
||||
|
||||
void stop_emission(guint id, GQuark detail)
|
||||
{
|
||||
g_signal_stop_emission(gobj_(), id, detail);
|
||||
}
|
||||
|
||||
void stop_emission_by_name(const gi::cstring_v signal)
|
||||
{
|
||||
g_signal_stop_emission_by_name(gobj_(), signal.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace GObject
|
||||
|
||||
template<>
|
||||
struct declare_cpptype_of<::GObject>
|
||||
{
|
||||
typedef repository::GObject::Object type;
|
||||
};
|
||||
|
||||
namespace GLib
|
||||
{
|
||||
// predefined
|
||||
typedef detail::callback<void(), gi::transfer_none_t> DestroyNotify;
|
||||
} // namespace GLib
|
||||
|
||||
} // namespace repository
|
||||
|
||||
// type safe signal connection
|
||||
template<typename T, typename Base = repository::GObject::Object>
|
||||
class signal_proxy;
|
||||
|
||||
template<typename R, typename Instance, typename... Args, typename Base>
|
||||
class signal_proxy<R(Instance, Args...), Base>
|
||||
{
|
||||
protected:
|
||||
typedef R(CppSig)(Instance, Args...);
|
||||
Base object_;
|
||||
gi::cstring name_;
|
||||
|
||||
public:
|
||||
typedef CppSig function_type;
|
||||
typedef detail::connectable<function_type> slot_type;
|
||||
|
||||
signal_proxy(Base owner, gi::cstring name)
|
||||
: object_(owner), name_(std::move(name))
|
||||
{}
|
||||
|
||||
template<typename Functor>
|
||||
gulong connect(Functor &&f)
|
||||
{
|
||||
return object_.template connect<CppSig>(name_, std::forward<Functor>(f));
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
gulong connect_after(Functor &&f)
|
||||
{
|
||||
return object_.template connect_after<CppSig>(
|
||||
name_, std::forward<Functor>(f));
|
||||
}
|
||||
|
||||
R emit(Args... args)
|
||||
{
|
||||
return object_.template emit<R, false, Args...>(
|
||||
name_, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
slot_type slot(Functor &&f)
|
||||
{
|
||||
return slot_type(std::forward<Functor>(f));
|
||||
}
|
||||
};
|
||||
|
||||
// type safe property setting
|
||||
template<typename T, typename Base = repository::GObject::Object>
|
||||
class property_proxy
|
||||
{
|
||||
typedef property_proxy self;
|
||||
typedef repository::GObject::ParamSpec ParamSpec;
|
||||
|
||||
protected:
|
||||
Base object_;
|
||||
ParamSpec pspec_;
|
||||
|
||||
public:
|
||||
property_proxy(Base owner, ParamSpec pspec) : object_(owner), pspec_(pspec) {}
|
||||
|
||||
property_proxy(Base owner, const gi::cstring_v name)
|
||||
: property_proxy(owner, owner.find_property(name, true))
|
||||
{}
|
||||
|
||||
void set(T v) { object_.set_property(pspec_, std::move(v)); }
|
||||
|
||||
self &operator=(T v)
|
||||
{
|
||||
set(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T get() const
|
||||
{
|
||||
return object_.template get_property<T>(pspec_.gobj_()->name);
|
||||
}
|
||||
|
||||
ParamSpec param_spec() const { return pspec_; }
|
||||
|
||||
signal_proxy<void(Base, ParamSpec)> signal_notify() const
|
||||
{
|
||||
return signal_proxy<void(Base, ParamSpec)>(
|
||||
object_, gi::cstring_v("notify::") + gi::cstring_v(pspec_.name_()));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename Base = repository::GObject::Object>
|
||||
class property_proxy_read : private property_proxy<T, Base>
|
||||
{
|
||||
typedef property_proxy<T, Base> super;
|
||||
|
||||
public:
|
||||
using super::get;
|
||||
using super::property_proxy;
|
||||
};
|
||||
|
||||
template<typename T, typename Base = repository::GObject::Object>
|
||||
class property_proxy_write : private property_proxy<T, Base>
|
||||
{
|
||||
typedef property_proxy<T, Base> super;
|
||||
|
||||
public:
|
||||
using super::property_proxy;
|
||||
using super::set;
|
||||
using super::operator=;
|
||||
};
|
||||
|
||||
// interface (ptr) is wrapped the same way,
|
||||
// as it is essentially a ptr to implementing object
|
||||
// TODO use other intermediate base ??
|
||||
using InterfaceBase = repository::GObject::Object;
|
||||
|
||||
namespace repository
|
||||
{
|
||||
namespace GObject
|
||||
{
|
||||
// connection helpers
|
||||
namespace internal
|
||||
{
|
||||
class SignalConnection : public detail::connection_impl
|
||||
{
|
||||
public:
|
||||
SignalConnection(gulong id, detail::connection_status s, Object object)
|
||||
: connection_impl(id, s), object_(object)
|
||||
{}
|
||||
|
||||
void disconnect() { object_.disconnect(id_); }
|
||||
|
||||
private:
|
||||
Object object_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
using SignalConnection = detail::connection<internal::SignalConnection>;
|
||||
using SignalScopedConnection = detail::scoped_connection<SignalConnection>;
|
||||
|
||||
} // namespace GObject
|
||||
|
||||
} // namespace repository
|
||||
|
||||
// connection callback type
|
||||
template<typename G>
|
||||
using slot = detail::connectable<G>;
|
||||
|
||||
template<typename G>
|
||||
inline repository::GObject::SignalConnection
|
||||
make_connection(
|
||||
gulong id, const gi::slot<G> &s, repository::GObject::Object object)
|
||||
{
|
||||
using repository::GObject::SignalConnection;
|
||||
return SignalConnection(id, s.connection(), object);
|
||||
}
|
||||
|
||||
} // namespace gi
|
||||
|
||||
#endif // GI_OBJECT_HPP
|
||||
172
cmake/external/glib/cppgir/gi/objectbase.hpp
vendored
Normal file
172
cmake/external/glib/cppgir/gi/objectbase.hpp
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
#ifndef GI_OBJECTBASE_HPP
|
||||
#define GI_OBJECTBASE_HPP
|
||||
|
||||
#include "gi_inc.hpp"
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
struct GObjectFuncs
|
||||
{
|
||||
static void *ref(void *data) { return g_object_ref(data); }
|
||||
static void *sink(void *data) { return g_object_ref_sink(data); }
|
||||
static void free(void *data) { g_object_unref(data); }
|
||||
static void *float_(void *data)
|
||||
{
|
||||
g_object_force_floating((GObject *)data);
|
||||
return g_object_ref(data);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Funcs, typename CType = void>
|
||||
class Wrapper
|
||||
{
|
||||
protected:
|
||||
CType *data_ = nullptr;
|
||||
|
||||
static CType *_ref(CType *data) { return data ? Funcs::ref(data) : data; }
|
||||
static CType *_sink(CType *data) { return data ? Funcs::sink(data) : data; }
|
||||
static CType *_float(CType *data)
|
||||
{
|
||||
return data ? Funcs::float_(data) : data;
|
||||
}
|
||||
static void _deleter(CType *&data)
|
||||
{
|
||||
if (data)
|
||||
Funcs::free(data);
|
||||
}
|
||||
|
||||
public:
|
||||
Wrapper(decltype(data_) d = nullptr, bool own = true, bool sink = true)
|
||||
: data_(own ? d : (sink ? _sink(d) : _ref(d)))
|
||||
{}
|
||||
|
||||
~Wrapper() { _deleter(data_); }
|
||||
|
||||
Wrapper(const Wrapper &other)
|
||||
{
|
||||
_deleter(data_);
|
||||
data_ = _ref(other.data_);
|
||||
}
|
||||
|
||||
Wrapper(Wrapper &&other) noexcept
|
||||
{
|
||||
_deleter(data_);
|
||||
data_ = other.data_;
|
||||
other.data_ = nullptr;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return (bool)data_; }
|
||||
|
||||
Wrapper &operator=(const Wrapper &other)
|
||||
{
|
||||
if (&other != this) {
|
||||
_deleter(data_);
|
||||
data_ = _ref(other.data_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Wrapper &operator=(Wrapper &&other) noexcept
|
||||
{
|
||||
if (&other != this) {
|
||||
_deleter(data_);
|
||||
data_ = other.data_;
|
||||
other.data_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Wrapper &other) const { return data_ == other.data_; }
|
||||
|
||||
bool operator==(std::nullptr_t o) const
|
||||
{
|
||||
(void)o;
|
||||
return data_ == o;
|
||||
}
|
||||
|
||||
bool operator!=(const Wrapper &other) const { return data_ != other.data_; }
|
||||
|
||||
bool operator!=(std::nullptr_t o) const { return data_ != o; }
|
||||
|
||||
CType *gobj_() { return this->data_; }
|
||||
const CType *gobj_() const { return this->data_; }
|
||||
};
|
||||
|
||||
class wrapper_tag
|
||||
{};
|
||||
|
||||
template<typename CType, typename Funcs, GType GTYPE_>
|
||||
class WrapperBase : public Wrapper<Funcs>, public wrapper_tag
|
||||
{
|
||||
typedef WrapperBase self;
|
||||
typedef Wrapper<Funcs> super_type;
|
||||
|
||||
public:
|
||||
typedef CType BaseObjectType;
|
||||
|
||||
BaseObjectType *gobj_() { return (BaseObjectType *)this->data_; }
|
||||
const BaseObjectType *gobj_() const
|
||||
{
|
||||
return (const BaseObjectType *)this->data_;
|
||||
}
|
||||
BaseObjectType *gobj_copy_() const
|
||||
{
|
||||
return (BaseObjectType *)self::_ref(this->data_);
|
||||
}
|
||||
BaseObjectType *gobj_float_() const
|
||||
{
|
||||
return (BaseObjectType *)self::_float(this->data_);
|
||||
}
|
||||
BaseObjectType *release_()
|
||||
{
|
||||
void *r = nullptr;
|
||||
std::swap(this->data_, r);
|
||||
return (BaseObjectType *)r;
|
||||
}
|
||||
|
||||
static GType get_type_() { return GTYPE_; }
|
||||
GType gobj_type_() { return GTYPE_; }
|
||||
|
||||
WrapperBase(BaseObjectType *p = nullptr, bool own = true, bool argout = true)
|
||||
: super_type(p, own, argout)
|
||||
{}
|
||||
|
||||
WrapperBase(const self &other) = default;
|
||||
WrapperBase(self &&other) = default;
|
||||
|
||||
self &operator=(const self &other) = default;
|
||||
self &operator=(self &&other) = default;
|
||||
|
||||
// always arrange to sink by default nowadays
|
||||
template<typename Cpp>
|
||||
static Cpp wrap(
|
||||
const typename Cpp::BaseObjectType *obj, bool own, bool argout = true)
|
||||
{
|
||||
static_assert(sizeof(Cpp) == sizeof(self), "type wrap not supported");
|
||||
static_assert(std::is_base_of<self, Cpp>::value, "type wrap not supported");
|
||||
WrapperBase w((self::BaseObjectType *)(obj), own, argout);
|
||||
return std::move(*static_cast<Cpp *>(&w));
|
||||
}
|
||||
};
|
||||
|
||||
typedef WrapperBase<void, GObjectFuncs, G_TYPE_NONE> ObjectBase;
|
||||
|
||||
// foundation for Variant that will be generated
|
||||
struct GVariantFuncs
|
||||
{
|
||||
static void *ref(void *data) { return g_variant_ref((GVariant *)data); }
|
||||
static void *sink(void *data) { return g_variant_ref_sink((GVariant *)data); }
|
||||
static void free(void *data) { g_variant_unref((GVariant *)data); }
|
||||
static void *float_(void *data) { return data; }
|
||||
};
|
||||
|
||||
typedef WrapperBase<GVariant, GVariantFuncs, G_TYPE_VARIANT> VariantWrapper;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace gi
|
||||
|
||||
#endif // GI_OBJECTBASE_HPP
|
||||
1562
cmake/external/glib/cppgir/gi/objectclass.hpp
vendored
Normal file
1562
cmake/external/glib/cppgir/gi/objectclass.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
242
cmake/external/glib/cppgir/gi/paramspec.hpp
vendored
Normal file
242
cmake/external/glib/cppgir/gi/paramspec.hpp
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
#ifndef GI_PARAMSPEC_HPP
|
||||
#define GI_PARAMSPEC_HPP
|
||||
|
||||
#include "objectbase.hpp"
|
||||
#include "value.hpp"
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
// slightly nasty; will be generated
|
||||
namespace repository
|
||||
{
|
||||
namespace GObject
|
||||
{
|
||||
enum class ParamFlags : std::underlying_type<::GParamFlags>::type;
|
||||
}
|
||||
} // namespace repository
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct GParamSpecFuncs
|
||||
{
|
||||
static void *ref(void *data) { return g_param_spec_ref((GParamSpec *)data); }
|
||||
static void *sink(void *data)
|
||||
{
|
||||
return g_param_spec_ref_sink((GParamSpec *)data);
|
||||
}
|
||||
static void free(void *data) { g_param_spec_unref((GParamSpec *)data); }
|
||||
static void *float_(void *data) { return data; }
|
||||
};
|
||||
|
||||
using ParamFlags = repository::GObject::ParamFlags;
|
||||
|
||||
// helper paramspec type
|
||||
template<typename T>
|
||||
struct param_spec_constructor;
|
||||
|
||||
#define GI_DECLARE_PARAM_SPEC(cpptype, suffix) \
|
||||
template<> \
|
||||
struct param_spec_constructor<cpptype> \
|
||||
{ \
|
||||
typedef std::true_type range_type; \
|
||||
static const constexpr decltype(&g_param_spec_##suffix) new_ = \
|
||||
g_param_spec_##suffix; \
|
||||
};
|
||||
|
||||
GI_DECLARE_PARAM_SPEC(char, char)
|
||||
GI_DECLARE_PARAM_SPEC(unsigned char, uchar)
|
||||
GI_DECLARE_PARAM_SPEC(int, int)
|
||||
GI_DECLARE_PARAM_SPEC(unsigned int, uint)
|
||||
GI_DECLARE_PARAM_SPEC(long, long)
|
||||
GI_DECLARE_PARAM_SPEC(unsigned long, ulong)
|
||||
GI_DECLARE_PARAM_SPEC(long long, int64)
|
||||
GI_DECLARE_PARAM_SPEC(unsigned long long, uint64)
|
||||
GI_DECLARE_PARAM_SPEC(float, float)
|
||||
GI_DECLARE_PARAM_SPEC(double, double)
|
||||
|
||||
#undef GI_DECLARE_PARAM_SPEC
|
||||
|
||||
// specialize appropriately with static new_ member
|
||||
template<typename T, typename Enable = void>
|
||||
struct ParamSpecFactory;
|
||||
|
||||
template<typename T>
|
||||
struct ParamSpecFactory<T,
|
||||
typename std::enable_if<param_spec_constructor<T>::range_type::value>::type>
|
||||
{
|
||||
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
|
||||
const gi::cstring_v blurb, T min, T max, T _default,
|
||||
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
|
||||
{
|
||||
return param_spec_constructor<T>::new_(name.c_str(), nick.c_str(),
|
||||
blurb.c_str(), min, max, _default, (GParamFlags)flags);
|
||||
}
|
||||
|
||||
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
|
||||
const gi::cstring_v blurb, T min, T max,
|
||||
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
|
||||
{
|
||||
return new_(name, nick, blurb, min, max, T{}, flags);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamSpecFactory<bool>
|
||||
{
|
||||
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
|
||||
const gi::cstring_v blurb, bool _default,
|
||||
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
|
||||
{
|
||||
return g_param_spec_boolean(name.c_str(), nick.c_str(), blurb.c_str(),
|
||||
_default, (GParamFlags)flags);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamSpecFactory<gpointer>
|
||||
{
|
||||
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
|
||||
const gi::cstring_v blurb,
|
||||
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
|
||||
{
|
||||
return g_param_spec_pointer(
|
||||
name.c_str(), nick.c_str(), blurb.c_str(), (GParamFlags)flags);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamSpecFactory<std::string>
|
||||
{
|
||||
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
|
||||
const gi::cstring_v blurb, const gi::cstring_v _default,
|
||||
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
|
||||
{
|
||||
return g_param_spec_string(name.c_str(), nick.c_str(), blurb.c_str(),
|
||||
_default.c_str(), (GParamFlags)flags);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamSpecFactory<gi::cstring> : public ParamSpecFactory<std::string>
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct ParamSpecFactory<T,
|
||||
typename std::enable_if<traits::is_object<T>::value>::type>
|
||||
{
|
||||
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
|
||||
const gi::cstring_v blurb,
|
||||
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
|
||||
{
|
||||
return g_param_spec_object(name.c_str(), nick.c_str(), blurb.c_str(),
|
||||
traits::gtype<T>::get_type(), (GParamFlags)flags);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ParamSpecFactory<T,
|
||||
typename std::enable_if<traits::is_boxed<T>::value>::type>
|
||||
{
|
||||
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
|
||||
const gi::cstring_v blurb,
|
||||
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
|
||||
{
|
||||
return g_param_spec_boxed(name.c_str(), nick.c_str(), blurb.c_str(),
|
||||
traits::gtype<T>::get_type(), (GParamFlags)flags);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ParamSpecFactory<T,
|
||||
typename std::enable_if<traits::is_enum_or_bitfield<T>::value>::type>
|
||||
{
|
||||
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
|
||||
const gi::cstring_v blurb, guint _default = 0,
|
||||
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
|
||||
{
|
||||
GType t = traits::gtype<T>::get_type();
|
||||
// FIXME compile-time determination rather than dynamic ??
|
||||
return G_TYPE_IS_FLAGS(t)
|
||||
? g_param_spec_flags(name.c_str(), nick.c_str(), blurb.c_str(),
|
||||
t, _default, (GParamFlags)flags)
|
||||
: g_param_spec_enum(name.c_str(), nick.c_str(), blurb.c_str(), t,
|
||||
_default, (GParamFlags)flags);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace repository
|
||||
{
|
||||
// slightly nasty
|
||||
namespace GObject
|
||||
{
|
||||
class ParamSpec;
|
||||
}
|
||||
|
||||
template<>
|
||||
struct declare_cpptype_of<GParamSpec>
|
||||
{
|
||||
typedef GObject::ParamSpec type;
|
||||
};
|
||||
|
||||
namespace GObject
|
||||
{
|
||||
class ParamSpec : public detail::WrapperBase<GParamSpec,
|
||||
detail::GParamSpecFuncs, G_TYPE_PARAM>
|
||||
{
|
||||
typedef WrapperBase<GParamSpec, detail::GParamSpecFuncs, G_TYPE_PARAM> super;
|
||||
|
||||
public:
|
||||
ParamSpec(std::nullptr_t = nullptr) {}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
static ParamSpec new_(Args &&...args)
|
||||
{
|
||||
return static_cast<ParamSpec &&>(
|
||||
super(detail::ParamSpecFactory<T>::new_(std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
// special override case
|
||||
static ParamSpec new_(const gi::cstring_v name, ParamSpec overridden)
|
||||
{
|
||||
return static_cast<ParamSpec &&>(
|
||||
super(g_param_spec_override(name.c_str(), overridden.gobj_())));
|
||||
}
|
||||
|
||||
gi::cstring_v get_blurb() { return g_param_spec_get_blurb(gobj_()); }
|
||||
|
||||
gi::cstring_v get_nick() { return g_param_spec_get_nick(gobj_()); }
|
||||
|
||||
gi::cstring_v get_name() { return g_param_spec_get_name(gobj_()); }
|
||||
|
||||
GQuark get_name_quark() { return g_param_spec_get_name_quark(gobj_()); }
|
||||
|
||||
repository::GObject::Value get_default_value()
|
||||
{
|
||||
return gi::wrap(g_param_spec_get_default_value(gobj_()), transfer_none);
|
||||
}
|
||||
|
||||
ParamSpec get_redirect_target()
|
||||
{
|
||||
return gi::wrap(g_param_spec_get_redirect_target(gobj_()), transfer_none);
|
||||
}
|
||||
|
||||
// struct fields
|
||||
const gchar *name_() const { return gobj_()->name; }
|
||||
|
||||
ParamFlags value_type() const { return (ParamFlags)gobj_()->flags; }
|
||||
|
||||
GType value_type_() const { return gobj_()->value_type; }
|
||||
|
||||
GType owner_type_() const { return gobj_()->owner_type; }
|
||||
};
|
||||
|
||||
} // namespace GObject
|
||||
|
||||
} // namespace repository
|
||||
|
||||
} // namespace gi
|
||||
|
||||
#endif // GI_PARAMSPEC_HPP
|
||||
874
cmake/external/glib/cppgir/gi/string.hpp
vendored
Normal file
874
cmake/external/glib/cppgir/gi/string.hpp
vendored
Normal file
@@ -0,0 +1,874 @@
|
||||
#ifndef GI_STRING_HPP
|
||||
#define GI_STRING_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
#ifdef __has_builtin
|
||||
#define GI_HAS_BUILTIN(x) __has_builtin(x)
|
||||
#else
|
||||
#define GI_HAS_BUILTIN(x) 0
|
||||
#endif
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
namespace convert
|
||||
{
|
||||
// generic template; specialize as needed where appropriate
|
||||
template<typename From, typename To, class Enable = void>
|
||||
struct converter
|
||||
{
|
||||
// fail in explanatory way if we end up here
|
||||
static To convert(const From &)
|
||||
{
|
||||
static_assert(!std::is_void<Enable>::value, "unknown type conversion");
|
||||
return To();
|
||||
}
|
||||
};
|
||||
|
||||
// implementation should provide some types
|
||||
template<typename From, typename To, class Enable = void>
|
||||
struct converter_base : public std::true_type
|
||||
{
|
||||
typedef From from_type;
|
||||
typedef To to_type;
|
||||
};
|
||||
|
||||
// conversion check for complete type
|
||||
template<typename From, typename To, typename Enable = void>
|
||||
struct is_convertible_impl : public std::false_type
|
||||
{};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct is_convertible_impl<From, To,
|
||||
typename std::enable_if<std::is_base_of<To, From>::value>::type>
|
||||
: public std::false_type
|
||||
{};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct is_convertible_impl<From, To,
|
||||
typename std::enable_if<!std::is_base_of<To, From>::value &&
|
||||
std::is_pointer<typename converter<From,
|
||||
To>::from_type *>::value>::type>
|
||||
: public std::true_type
|
||||
{};
|
||||
|
||||
template<typename From, typename To, bool complete>
|
||||
struct is_convertible_pre
|
||||
{
|
||||
using type = std::false_type;
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct is_convertible_pre<From, To, true>
|
||||
{
|
||||
using type = is_convertible_impl<From, To>;
|
||||
};
|
||||
|
||||
// check whether conversion possible
|
||||
// reject incomplete forward declared types
|
||||
template<typename From, typename To, typename Enable = void>
|
||||
struct is_convertible : public is_convertible_pre<From, To,
|
||||
gi::traits::is_type_complete<To>::value>::type
|
||||
{};
|
||||
} // namespace convert
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// tag
|
||||
|
||||
struct String
|
||||
{};
|
||||
|
||||
template<typename Transfer>
|
||||
struct StringFuncs
|
||||
{
|
||||
static void _deleter(char *&p)
|
||||
{
|
||||
if (Transfer().value)
|
||||
g_free(p);
|
||||
p = nullptr;
|
||||
}
|
||||
static void _copy(const char *p)
|
||||
{
|
||||
return Transfer().value ? g_strdup(p) : p;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Transfer>
|
||||
class cstr : public String
|
||||
{
|
||||
using self_type = cstr;
|
||||
|
||||
protected:
|
||||
using _member_type =
|
||||
typename std::conditional<std::is_same<Transfer, transfer_full_t>::value,
|
||||
char, const char>::type;
|
||||
|
||||
_member_type *data_ = nullptr;
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (Transfer().value && data_)
|
||||
g_free((char *)data_);
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
using traits_type = std::char_traits<char>;
|
||||
using value_type = char;
|
||||
using pointer = char *;
|
||||
using const_pointer = const char *;
|
||||
using reference = char &;
|
||||
using const_reference = const char &;
|
||||
using const_iterator = const char *;
|
||||
using iterator = const_iterator;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using reverse_iterator = const_reverse_iterator;
|
||||
using size_type = size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
using view_type = cstr<transfer_none_t>;
|
||||
static constexpr bool is_view_type =
|
||||
!std::is_same<Transfer, transfer_full_t>::value;
|
||||
|
||||
static constexpr size_type npos = static_cast<size_type>(-1);
|
||||
|
||||
constexpr cstr() noexcept : data_(nullptr) {}
|
||||
|
||||
constexpr cstr(std::nullptr_t) noexcept : data_(nullptr) {}
|
||||
|
||||
// const usually means none, so only on view type
|
||||
// also, no arbitrary size is accepted here
|
||||
template<typename Enable = void,
|
||||
typename std::enable_if<std::is_same<Enable, void>::value &&
|
||||
is_view_type>::type * = nullptr>
|
||||
constexpr cstr(const char *data) : data_(data)
|
||||
{}
|
||||
|
||||
// a single pointer, optionally specify ownership
|
||||
// behave like string by default, assume no ownership of incoming pointer
|
||||
template<typename LTransfer = transfer_none_t,
|
||||
typename std::enable_if<
|
||||
(std::is_same<LTransfer, transfer_full_t>::value ||
|
||||
std::is_same<LTransfer, transfer_none_t>::value) &&
|
||||
!is_view_type>::type * = nullptr>
|
||||
cstr(char *data, const LTransfer &t)
|
||||
: data_((t.value == transfer_full.value) || !data ? data : g_strdup(data))
|
||||
{}
|
||||
|
||||
// pointer along with size
|
||||
// always behave like string and make a copy
|
||||
template<typename Enable = void,
|
||||
typename std::enable_if<std::is_same<Enable, void>::value &&
|
||||
!is_view_type>::type * = nullptr>
|
||||
cstr(const char *data, size_t len = npos)
|
||||
: data_(!data ? nullptr
|
||||
: (len == npos ? g_strdup(data) : g_strndup(data, len)))
|
||||
{}
|
||||
|
||||
// construct from any transfer variant
|
||||
template<typename OTransfer>
|
||||
cstr(const cstr<OTransfer> &s) : cstr(s.data())
|
||||
{}
|
||||
|
||||
// accept from string, but NOT string_view as that may not be null-terminated
|
||||
template<typename Allocator>
|
||||
cstr(const std::basic_string<char, std::char_traits<char>, Allocator>
|
||||
&s) noexcept
|
||||
: cstr(s.data())
|
||||
{}
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
// some optional variants of above
|
||||
constexpr cstr(std::nullopt_t) noexcept : data_(nullptr) {}
|
||||
|
||||
template<typename Allocator>
|
||||
cstr(const std::optional<
|
||||
std::basic_string<char, std::char_traits<char>, Allocator>> &s) noexcept
|
||||
: cstr(s ? s.value().data() : nullptr)
|
||||
{}
|
||||
#endif
|
||||
|
||||
// hook extensible conversion
|
||||
// (avoid instantiation and confusion with self and base types)
|
||||
template<typename From,
|
||||
typename NoBase = typename std::enable_if<
|
||||
!std::is_base_of<self_type, From>::value>::type,
|
||||
typename Enable = typename std::enable_if<
|
||||
convert::is_convertible<From, self_type>::value>::type>
|
||||
cstr(const From &f) : cstr(convert::converter<From, self_type>::convert(f))
|
||||
{}
|
||||
|
||||
// custom
|
||||
constexpr const_pointer gobj_() const { return data_; }
|
||||
|
||||
explicit constexpr operator bool() const { return data_; }
|
||||
|
||||
_member_type *release_()
|
||||
{
|
||||
auto tmp = this->data_;
|
||||
this->data_ = nullptr;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// destruct / copy / assign
|
||||
~cstr() { clear(); }
|
||||
|
||||
cstr(const cstr &other) { *this = other; }
|
||||
|
||||
cstr(cstr &&other) { *this = std::move(other); }
|
||||
|
||||
cstr &operator=(const cstr &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
clear();
|
||||
data_ = Transfer().value ? g_strdup(other.data_) : other.data_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
cstr &operator=(cstr &&other)
|
||||
{
|
||||
if (this != &other) {
|
||||
clear();
|
||||
data_ = other.data_;
|
||||
other.data_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// deduced conversion to string(_view)
|
||||
template<typename Destination,
|
||||
typename Check = typename std::enable_if<
|
||||
convert::is_convertible<self_type, Destination>::value>::type>
|
||||
operator Destination() const
|
||||
{
|
||||
return convert::converter<self_type, Destination>::convert(*this);
|
||||
}
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
// unfortunately, conversion to std::optional picks Destination = std::string
|
||||
// (which can obviously not be excluded above, or as specialization)
|
||||
std::optional<std::string> opt_()
|
||||
{
|
||||
using To = std::optional<std::string>;
|
||||
return c_str() ? To({c_str(), size()}) : To(std::nullopt);
|
||||
}
|
||||
#endif
|
||||
|
||||
// usual string(view) stuff
|
||||
|
||||
// iterators
|
||||
|
||||
constexpr const_iterator begin() const noexcept { return data_; }
|
||||
|
||||
constexpr const_iterator end() const noexcept
|
||||
{
|
||||
return data_ ? data_ + size() : nullptr;
|
||||
}
|
||||
|
||||
constexpr const_iterator cbegin() const noexcept { return begin(); }
|
||||
|
||||
constexpr const_iterator cend() const noexcept { return end(); }
|
||||
|
||||
const_reverse_iterator rbegin() const noexcept
|
||||
{
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
const_reverse_iterator rend() const noexcept
|
||||
{
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
const_reverse_iterator crbegin() const noexcept { return rbegin(); }
|
||||
|
||||
const_reverse_iterator crend() const noexcept { return rend(); }
|
||||
|
||||
// capacity
|
||||
|
||||
constexpr size_type size() const noexcept
|
||||
{
|
||||
#if GI_HAS_BUILTIN(__builtin_strlen) || \
|
||||
(defined(__GNUC__) && !defined(__clang__))
|
||||
return __builtin_strlen(data_);
|
||||
#else
|
||||
return data_ ? strlen(data_) : 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr size_type length() const noexcept { return size(); }
|
||||
|
||||
constexpr size_type max_size() const noexcept
|
||||
{
|
||||
return std::numeric_limits<size_type>::max();
|
||||
}
|
||||
|
||||
constexpr bool empty() const noexcept { return !data_ || *data_ == 0; }
|
||||
|
||||
// access
|
||||
|
||||
constexpr const_reference operator[](size_type i) const { return data_[i]; }
|
||||
|
||||
constexpr const_reference at(size_type i) const
|
||||
{
|
||||
return i < size() ? data_[i]
|
||||
: (try_throw(std::out_of_range("cstr::at")), data_[i]);
|
||||
}
|
||||
|
||||
constexpr const_reference front() const { return data_[0]; }
|
||||
|
||||
constexpr const_reference back() const { return data_[size() - 1]; }
|
||||
|
||||
constexpr const_pointer data() const noexcept { return data_; }
|
||||
|
||||
constexpr const_pointer c_str() const noexcept { return data_; }
|
||||
|
||||
// modifiers
|
||||
|
||||
// view only
|
||||
template<typename Enable = void,
|
||||
typename std::enable_if<std::is_same<Enable, void>::value &&
|
||||
is_view_type>::type * = nullptr>
|
||||
constexpr void remove_prefix(size_type n)
|
||||
{
|
||||
data_ += n;
|
||||
}
|
||||
|
||||
constexpr void swap(self_type &s) noexcept
|
||||
{
|
||||
std::swap(this->data_, s.data_);
|
||||
}
|
||||
|
||||
// operations
|
||||
|
||||
size_type copy(char *buf, size_type n, size_type pos = 0) const
|
||||
{
|
||||
auto s = size();
|
||||
if (pos > s)
|
||||
try_throw(std::out_of_range("cstr::copy"));
|
||||
size_type rlen = (std::min)(s - pos, n);
|
||||
if (rlen > 0) {
|
||||
const char *start = data_ + pos;
|
||||
traits_type::copy(buf, start, rlen);
|
||||
}
|
||||
return rlen;
|
||||
}
|
||||
|
||||
int compare(view_type x) const noexcept
|
||||
{
|
||||
return g_strcmp0(data_, x.c_str());
|
||||
}
|
||||
|
||||
int compare(const char *s) const { return compare(view_type(s)); }
|
||||
|
||||
// find
|
||||
|
||||
size_type find(view_type n, size_type pos = 0) const noexcept
|
||||
{
|
||||
auto s = size();
|
||||
auto os = n.size();
|
||||
if (os > s || pos > s - os)
|
||||
return npos;
|
||||
if (!os)
|
||||
return pos <= s ? pos : npos;
|
||||
auto loc = strstr(data_ + pos, n.data());
|
||||
return loc ? loc - data_ : npos;
|
||||
}
|
||||
|
||||
size_type find(char c, size_type pos = 0) const noexcept
|
||||
{
|
||||
if (pos >= size())
|
||||
return npos;
|
||||
auto loc = strchr(data_ + pos, c);
|
||||
return loc ? loc - data_ : npos;
|
||||
}
|
||||
|
||||
size_type find(const char *s, size_type pos = 0) const
|
||||
{
|
||||
return find(view_type(s), pos);
|
||||
}
|
||||
|
||||
size_type rfind(view_type n, size_type pos = npos) const noexcept
|
||||
{
|
||||
auto s = size();
|
||||
if (!s)
|
||||
return npos;
|
||||
auto os = n.size();
|
||||
if (!os)
|
||||
return pos == npos ? s : pos;
|
||||
auto loc = g_strrstr_len(c_str(), pos, n.c_str());
|
||||
return loc ? loc - data_ : npos;
|
||||
}
|
||||
|
||||
size_type rfind(char c, size_type pos = npos) const noexcept
|
||||
{
|
||||
// not quite efficient, but anyways
|
||||
char str[] = {c, 0};
|
||||
return rfind(str, pos);
|
||||
}
|
||||
|
||||
size_type rfind(const char *s, size_type pos = npos) const
|
||||
{
|
||||
return rfind(view_type(s), pos);
|
||||
}
|
||||
|
||||
// find_first_of variants
|
||||
// only provide those if std helps us out
|
||||
#if __cplusplus >= 201703L
|
||||
size_type find_first_of(view_type s, size_type pos = 0) const noexcept
|
||||
{
|
||||
return std::string_view(data_).find_first_of(s.data(), pos);
|
||||
}
|
||||
|
||||
size_type find_first_of(char c, size_type pos = 0) const noexcept
|
||||
{
|
||||
return find(c, pos);
|
||||
}
|
||||
|
||||
size_type find_first_of(const char *s, size_type pos = 0) const
|
||||
{
|
||||
return find_first_of(view_type(s), pos);
|
||||
}
|
||||
|
||||
size_type find_last_of(view_type s, size_type pos = npos) const noexcept
|
||||
{
|
||||
return std::string_view(data_).find_last_of(s.data(), pos);
|
||||
}
|
||||
|
||||
size_type find_last_of(char c, size_type pos = npos) const noexcept
|
||||
{
|
||||
return rfind(c, pos);
|
||||
}
|
||||
|
||||
size_type find_last_of(const char *s, size_type pos = npos) const
|
||||
{
|
||||
return find_last_of(view_type(s), pos);
|
||||
}
|
||||
|
||||
size_type find_first_not_of(view_type s, size_type pos = 0) const noexcept
|
||||
{
|
||||
return std::string_view(data_).find_first_not_of(s.data(), pos);
|
||||
}
|
||||
|
||||
size_type find_first_not_of(char c, size_type pos = 0) const noexcept
|
||||
{
|
||||
return std::string_view(data_).find_first_not_of(c, pos);
|
||||
}
|
||||
|
||||
size_type find_first_not_of(const char *s, size_type pos = 0) const
|
||||
{
|
||||
return find_first_not_of(view_type(s), pos);
|
||||
}
|
||||
|
||||
size_type find_last_not_of(view_type s, size_type pos = npos) const noexcept
|
||||
{
|
||||
return std::string_view(data_).find_last_not_of(s.data(), pos);
|
||||
}
|
||||
|
||||
size_type find_last_not_of(char c, size_type pos = npos) const noexcept
|
||||
{
|
||||
return std::string_view(data_).find_last_not_of(c, pos);
|
||||
}
|
||||
|
||||
size_type find_last_not_of(const char *s, size_type pos = npos) const
|
||||
{
|
||||
return find_last_not_of(view_type(s), pos);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
using _string_view = cstr<transfer_none_t>;
|
||||
|
||||
inline bool
|
||||
operator==(_string_view x, _string_view y) noexcept
|
||||
{
|
||||
return x.compare(y) == 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(_string_view x, _string_view y) noexcept
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator<(_string_view x, _string_view y) noexcept
|
||||
{
|
||||
return x.compare(y) < 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator>(_string_view x, _string_view y) noexcept
|
||||
{
|
||||
return y < x;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator<=(_string_view x, _string_view y) noexcept
|
||||
{
|
||||
return !(y < x);
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator>=(_string_view x, _string_view y) noexcept
|
||||
{
|
||||
return !(x < y);
|
||||
}
|
||||
|
||||
#ifndef GI_NO_STRING_IOS
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &o, _string_view sv)
|
||||
{
|
||||
// backwards compatibility; behave similar to empty string
|
||||
return o << (sv ? sv.c_str() : "");
|
||||
}
|
||||
#endif
|
||||
|
||||
// purpose of silly Delay is to avoid gcc premature instantiation of converter
|
||||
// (in the hook constructor of base class with cstring as From ??)
|
||||
template<typename Delay = void>
|
||||
class cstring_d : public cstr<transfer_full_t>
|
||||
{
|
||||
using self_type = cstring_d;
|
||||
using super_type = cstr<transfer_full_t>;
|
||||
|
||||
public:
|
||||
using super_type::super_type;
|
||||
|
||||
// if all other construction fails, try to pass through string
|
||||
template<typename... Args,
|
||||
typename NoConvert = typename std::enable_if<
|
||||
!std::is_constructible<super_type, Args...>::value>::type,
|
||||
typename Enable = typename std::enable_if<
|
||||
std::is_constructible<std::string, Args...>::value>::type>
|
||||
cstring_d(Args &&...args)
|
||||
: super_type(std::string(std::forward<Args>(args)...))
|
||||
{}
|
||||
|
||||
constexpr pointer gobj_() { return data_; }
|
||||
|
||||
// re-use string
|
||||
template<typename... Args>
|
||||
self_type &assign(Args &&...t)
|
||||
{
|
||||
return *this = std::string().assign(std::forward<Args>(t)...);
|
||||
}
|
||||
|
||||
// access
|
||||
|
||||
using super_type::at;
|
||||
constexpr reference at(size_type i)
|
||||
{
|
||||
return i < size() ? data_[i]
|
||||
: (try_throw(std::out_of_range("cstr::at")), data_[i]);
|
||||
}
|
||||
|
||||
using super_type::front;
|
||||
constexpr reference front() { return data_[0]; }
|
||||
|
||||
using super_type::back;
|
||||
constexpr reference back() { return data_[size() - 1]; }
|
||||
|
||||
using super_type::data;
|
||||
constexpr pointer data() noexcept { return data_; }
|
||||
|
||||
// iterators
|
||||
|
||||
using super_type::begin;
|
||||
constexpr iterator begin() noexcept { return data_; }
|
||||
|
||||
using super_type::end;
|
||||
constexpr iterator end() noexcept { return data_ ? data_ + size() : nullptr; }
|
||||
|
||||
using super_type::rbegin;
|
||||
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
|
||||
|
||||
using super_type::rend;
|
||||
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
|
||||
|
||||
// operations
|
||||
|
||||
using super_type::clear;
|
||||
|
||||
void push_back(char c)
|
||||
{
|
||||
char str[] = {c, 0};
|
||||
self_type n{
|
||||
g_strconcat(c_str() ? c_str() : "", str, (char *)NULL), transfer_full};
|
||||
swap(n);
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
auto s = size();
|
||||
if (s)
|
||||
at(s - 1) = 0;
|
||||
}
|
||||
|
||||
self_type &append(const char *str)
|
||||
{
|
||||
self_type n{
|
||||
g_strconcat(c_str() ? c_str() : "", str, (char *)NULL), transfer_full};
|
||||
swap(n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Transfer>
|
||||
self_type &append(const cstr<Transfer> &str)
|
||||
{
|
||||
return append(str.data());
|
||||
}
|
||||
|
||||
// otherwise delegate
|
||||
template<typename... Args>
|
||||
self_type &append(Args &&...args)
|
||||
{
|
||||
std::string s;
|
||||
s.append(std::forward<Args>(args)...);
|
||||
// make sure to select the desired variant
|
||||
return append((const char *)s.c_str());
|
||||
}
|
||||
|
||||
// likewise for +=
|
||||
self_type &operator+=(char c)
|
||||
{
|
||||
push_back(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
self_type &operator+=(const T &o)
|
||||
{
|
||||
return append(o);
|
||||
}
|
||||
|
||||
self_type substr(size_type pos = 0, size_type n = npos) const
|
||||
{
|
||||
auto l = size();
|
||||
return (pos > l)
|
||||
? (try_throw(std::out_of_range("cstr::substr")), self_type())
|
||||
: self_type(data_ + pos, std::min(n, l - pos));
|
||||
}
|
||||
|
||||
// indeed some are lacking/skipped
|
||||
// only minimal compatibility
|
||||
|
||||
// custom; align with other cases
|
||||
self_type copy_() { return substr(0); }
|
||||
};
|
||||
|
||||
using cstring = cstring_d<>;
|
||||
|
||||
// likewise; (delayed) view type
|
||||
template<typename Delay = void>
|
||||
class cstring_v_d : public cstr<transfer_none_t>
|
||||
{
|
||||
using self_type = cstring_v_d;
|
||||
using super_type = cstr<transfer_none_t>;
|
||||
|
||||
public:
|
||||
using super_type::super_type;
|
||||
|
||||
// primary reason for subtype
|
||||
// provide copy/upgrade to owning variant
|
||||
cstring copy_() { return {g_strdup(this->data()), transfer_full}; }
|
||||
};
|
||||
|
||||
using cstring_v = cstring_v_d<>;
|
||||
|
||||
inline cstring
|
||||
operator+(const _string_view x, const _string_view y) noexcept
|
||||
{
|
||||
if (!x)
|
||||
return {g_strdup(y.c_str()), transfer_full};
|
||||
if (!y)
|
||||
return {g_strdup(x.c_str()), transfer_full};
|
||||
return {g_strconcat(x.c_str(), y.c_str(), (char *)NULL), transfer_full};
|
||||
}
|
||||
|
||||
inline cstring
|
||||
operator+(const _string_view x, char y) noexcept
|
||||
{
|
||||
if (!x)
|
||||
return {1, y};
|
||||
char str[] = {y, 0};
|
||||
return x + str;
|
||||
}
|
||||
|
||||
inline cstring
|
||||
operator+(char x, const _string_view y) noexcept
|
||||
{
|
||||
if (!y)
|
||||
return {1, x};
|
||||
char str[] = {x, 0};
|
||||
return str + y;
|
||||
}
|
||||
|
||||
// add convenient conversions
|
||||
|
||||
// local helper traits used below
|
||||
namespace trait
|
||||
{
|
||||
template<typename T, std::size_t SIZE, typename Enable = void>
|
||||
struct has_data_member : std::false_type
|
||||
{};
|
||||
|
||||
template<typename T, std::size_t SIZE>
|
||||
struct has_data_member<T, SIZE,
|
||||
typename std::enable_if<
|
||||
std::is_pointer<decltype(std::declval<T>().c_str())>::value>::type>
|
||||
: std::integral_constant<bool, sizeof(*std::declval<T>().c_str()) == SIZE>
|
||||
{};
|
||||
|
||||
template<typename T, typename Enable = void>
|
||||
struct has_size_member : std::false_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct has_size_member<T, typename std::enable_if<std::is_integral<
|
||||
decltype(std::declval<T>().size())>::value>::type>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct is_string_type
|
||||
: public std::integral_constant<bool,
|
||||
has_data_member<T, 1>::value && has_size_member<T>::value>
|
||||
{};
|
||||
|
||||
} // namespace trait
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace convert
|
||||
{
|
||||
template<typename From>
|
||||
struct converter<From, detail::cstr<transfer_full_t>,
|
||||
typename std::enable_if<detail::trait::is_string_type<From>::value>::type>
|
||||
: public converter_base<From, detail::cstr<transfer_full_t>>
|
||||
{
|
||||
static detail::cstring convert(const From &v)
|
||||
{
|
||||
return {(char *)v.c_str(), v.size()};
|
||||
}
|
||||
};
|
||||
|
||||
// to a typical string(_view) case
|
||||
// (avoid conflict with above)
|
||||
// the traits_type (tries to) restricts this to std::string(_view)
|
||||
// not doing so might conveniently allow conversion to other types as well.
|
||||
// however, this would also allow e.g. QByteArray, which comes with an operator+
|
||||
// in global namespace (also selected by non-ADL lookup),
|
||||
// which then results in ambiguous overload
|
||||
// (with the operator+ that is provided above)
|
||||
template<typename Transfer, typename To>
|
||||
struct converter<detail::cstr<Transfer>, To,
|
||||
typename std::enable_if<
|
||||
!std::is_base_of<detail::String, To>::value &&
|
||||
!std::is_same<typename To::traits_type, void>::value &&
|
||||
std::is_constructible<To, const char *, size_t>::value>::type>
|
||||
: public converter_base<detail::cstr<Transfer>, To>
|
||||
{
|
||||
static To convert(const detail::cstr<Transfer> &v)
|
||||
{
|
||||
return v.c_str() ? To{(char *)v.c_str(), v.size()} : To();
|
||||
}
|
||||
};
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
// to an std::optional string(_view) case
|
||||
template<typename Transfer, typename To>
|
||||
struct converter<detail::cstr<Transfer>, To,
|
||||
typename std::enable_if<std::is_constructible<To, std::in_place_t,
|
||||
const char *, size_t>::value>::type>
|
||||
: public converter_base<detail::cstr<Transfer>, To>
|
||||
{
|
||||
static To convert(const detail::cstr<Transfer> &v)
|
||||
{
|
||||
abort();
|
||||
return v.c_str() ? To{std::in_place, (char *)v.c_str(), v.size()} : To();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace convert
|
||||
|
||||
using detail::cstring;
|
||||
using detail::cstring_v;
|
||||
|
||||
// sanity check; match C counterpart
|
||||
static_assert(sizeof(cstring) == sizeof(char *), "");
|
||||
static_assert(sizeof(cstring_v) == sizeof(char *), "");
|
||||
|
||||
namespace traits
|
||||
{
|
||||
template<>
|
||||
struct ctype<const gi::cstring, void>
|
||||
{
|
||||
typedef const char *type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ctype<gi::cstring, void>
|
||||
{
|
||||
typedef char *type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ctype<const gi::cstring_v, void>
|
||||
{
|
||||
typedef const char *type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ctype<gi::cstring_v, void>
|
||||
{
|
||||
typedef char *type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct cpptype<char *, transfer_full_t>
|
||||
{
|
||||
using type = gi::cstring;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct cpptype<char *, transfer_none_t>
|
||||
{
|
||||
using type = gi::cstring_v;
|
||||
};
|
||||
|
||||
} // namespace traits
|
||||
|
||||
} // namespace gi
|
||||
|
||||
// specialize std::hash for suitable use
|
||||
GI_MODULE_EXPORT
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<gi::cstring>
|
||||
{
|
||||
typedef gi::cstring argument_type;
|
||||
typedef std::size_t result_type;
|
||||
result_type operator()(argument_type const &s) const
|
||||
{
|
||||
return s.c_str() ? g_str_hash(s.c_str()) : 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<gi::cstring_v>
|
||||
{
|
||||
typedef gi::cstring_v argument_type;
|
||||
typedef std::size_t result_type;
|
||||
result_type operator()(argument_type const &s) const
|
||||
{
|
||||
return s.c_str() ? g_str_hash(s.c_str()) : 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // GI_STRING_HPP
|
||||
545
cmake/external/glib/cppgir/gi/value.hpp
vendored
Normal file
545
cmake/external/glib/cppgir/gi/value.hpp
vendored
Normal file
@@ -0,0 +1,545 @@
|
||||
#ifndef GI_VALUE_HPP
|
||||
#define GI_VALUE_HPP
|
||||
|
||||
#include "exception.hpp"
|
||||
#include "wrap.hpp"
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
namespace repository
|
||||
{
|
||||
// specialize to declare gtype info
|
||||
// if not within class get_type()
|
||||
// gvalue info can also be included this way
|
||||
// to inject support into Value wrapper
|
||||
template<typename T>
|
||||
struct declare_gtype_of : public std::false_type
|
||||
{};
|
||||
|
||||
} // namespace repository
|
||||
|
||||
namespace traits
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template<typename T, class Enable = void>
|
||||
struct gtype : public std::false_type
|
||||
{
|
||||
static GType get_type()
|
||||
{
|
||||
// dummy test to trigger (almost) always
|
||||
static_assert(std::is_void<T>::value, "type is not a registered GType");
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// gboxed/gobject (or otherwise class) cases
|
||||
template<typename T>
|
||||
struct gtype<T, typename if_valid_type<decltype(T::get_type_())>::type>
|
||||
: public std::true_type
|
||||
{
|
||||
typedef typename std::remove_reference<T>::type CppType;
|
||||
static GType get_type() { return CppType::get_type_(); }
|
||||
};
|
||||
|
||||
// otherwise externally declared
|
||||
template<typename T>
|
||||
struct gtype<T, typename if_valid_type<decltype(repository::declare_gtype_of<
|
||||
T>::get_type())>::type> : public std::true_type
|
||||
{
|
||||
static constexpr GType (
|
||||
*get_type)() = repository::declare_gtype_of<T>::get_type;
|
||||
};
|
||||
|
||||
// gvalue helper info
|
||||
template<typename T, class Enable = void>
|
||||
struct gvalue : public std::false_type
|
||||
{};
|
||||
|
||||
// as declared (both of set_value and get_value or neither)
|
||||
template<typename T>
|
||||
struct gvalue<T,
|
||||
typename if_valid_type<decltype(repository::declare_gtype_of<T>::get_value(
|
||||
nullptr))>::type> : public std::true_type
|
||||
{
|
||||
static T get(const GValue *val)
|
||||
{
|
||||
return repository::declare_gtype_of<T>::get_value(val);
|
||||
}
|
||||
static void set(GValue *val, T t)
|
||||
{
|
||||
repository::declare_gtype_of<T>::set_value(val, t);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using is_enum_or_bitfield =
|
||||
typename std::conditional<std::is_enum<T>::value && gtype<T>::value,
|
||||
std::true_type, std::false_type>::type;
|
||||
|
||||
// handle enum/flags cases, rather than many declares
|
||||
template<typename T>
|
||||
struct gvalue<T, typename std::enable_if<is_enum_or_bitfield<T>::value>::type>
|
||||
: public std::true_type
|
||||
{
|
||||
static T get(const GValue *val)
|
||||
{
|
||||
GType t = gtype<T>::get_type();
|
||||
if (G_TYPE_IS_FLAGS(t))
|
||||
return static_cast<T>(g_value_get_flags(val));
|
||||
// assume enum, let glib complain otherwise
|
||||
return static_cast<T>(g_value_get_enum(val));
|
||||
}
|
||||
|
||||
static void set(GValue *val, T v)
|
||||
{
|
||||
GType t = gtype<T>::get_type();
|
||||
if (G_TYPE_IS_FLAGS(t)) {
|
||||
g_value_set_flags(val, (guint)v);
|
||||
} else {
|
||||
// assume enum, let glib complain otherwise
|
||||
g_value_set_enum(val, (gint)v);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
using gtype = detail::gtype<
|
||||
typename std::decay<typename std::remove_reference<T>::type>::type>;
|
||||
|
||||
template<typename T>
|
||||
using gvalue = detail::gvalue<
|
||||
typename std::decay<typename std::remove_reference<T>::type>::type>;
|
||||
|
||||
template<typename T>
|
||||
using is_enum_or_bitfield = detail::is_enum_or_bitfield<
|
||||
typename std::decay<typename std::remove_reference<T>::type>::type>;
|
||||
|
||||
#if 0
|
||||
template<typename T, typename Enable = void>
|
||||
struct is_flag : public std::false_type {};
|
||||
|
||||
// TODO extend fundamental type stuff ??
|
||||
template<typename T>
|
||||
struct is_flag<T,
|
||||
typename std::enable_if<std::is_enum<T>::value &&
|
||||
repository::declare_gtype_of<T>::get_fundamental_type() == G_TYPE_FLAGS>::type> :
|
||||
public std::true_type {};
|
||||
#endif
|
||||
|
||||
} // namespace traits
|
||||
|
||||
// C++ types are used below (e.g. int) instead of e.g. gint
|
||||
// since gint64 might map to same as glong (or not)
|
||||
// so some of the int types are "best approximation" from C++ type to GType
|
||||
// instead; use the following types to guide to the right overload
|
||||
typedef char vchar;
|
||||
typedef long vlong;
|
||||
typedef int vint;
|
||||
typedef long long vint64;
|
||||
typedef bool vboolean;
|
||||
|
||||
typedef unsigned char vuchar;
|
||||
typedef unsigned long vulong;
|
||||
typedef unsigned int vuint;
|
||||
typedef unsigned long long vuint64;
|
||||
|
||||
typedef float vfloat;
|
||||
typedef double vdouble;
|
||||
|
||||
// NOTE: (plain) char might be signed or unsigned depending on platform
|
||||
// but gchar == char always anyway
|
||||
static_assert(std::is_same<gchar, char>::value, "now what");
|
||||
|
||||
namespace repository
|
||||
{
|
||||
template<>
|
||||
struct declare_gtype_of<void>
|
||||
{
|
||||
static GType get_type() { return G_TYPE_NONE; }
|
||||
};
|
||||
|
||||
#define GI_DECLARE_GTYPE(cpptype, g_type) \
|
||||
template<> \
|
||||
struct declare_gtype_of<cpptype> \
|
||||
{ \
|
||||
static constexpr GType get_type() { return g_type; } \
|
||||
};
|
||||
|
||||
#define GI_DECLARE_GTYPE_VALUE(cpptype, g_type, value_suffix) \
|
||||
template<> \
|
||||
struct declare_gtype_of<cpptype> \
|
||||
{ \
|
||||
static constexpr GType get_type() { return g_type; } \
|
||||
static void set_value(GValue *val, cpptype v) \
|
||||
{ \
|
||||
g_value_set_##value_suffix(val, v); \
|
||||
} \
|
||||
static cpptype get_value(const GValue *val) \
|
||||
{ \
|
||||
return g_value_get_##value_suffix(val); \
|
||||
} \
|
||||
};
|
||||
|
||||
// declare non-cv qualified type
|
||||
GI_DECLARE_GTYPE_VALUE(gpointer, G_TYPE_POINTER, pointer)
|
||||
GI_DECLARE_GTYPE_VALUE(bool, G_TYPE_BOOLEAN, boolean)
|
||||
GI_DECLARE_GTYPE_VALUE(char, G_TYPE_CHAR, schar)
|
||||
GI_DECLARE_GTYPE_VALUE(unsigned char, G_TYPE_UCHAR, uchar)
|
||||
GI_DECLARE_GTYPE_VALUE(int, G_TYPE_INT, int)
|
||||
GI_DECLARE_GTYPE_VALUE(unsigned int, G_TYPE_UINT, uint)
|
||||
GI_DECLARE_GTYPE_VALUE(long, G_TYPE_LONG, long)
|
||||
GI_DECLARE_GTYPE_VALUE(unsigned long, G_TYPE_ULONG, ulong)
|
||||
GI_DECLARE_GTYPE_VALUE(long long, G_TYPE_INT64, int64)
|
||||
GI_DECLARE_GTYPE_VALUE(unsigned long long, G_TYPE_UINT64, uint64)
|
||||
GI_DECLARE_GTYPE_VALUE(float, G_TYPE_FLOAT, float)
|
||||
GI_DECLARE_GTYPE_VALUE(double, G_TYPE_DOUBLE, double)
|
||||
// some custom set/get for these
|
||||
// remember; the pointer is non-const
|
||||
GI_DECLARE_GTYPE(const char *, G_TYPE_STRING)
|
||||
GI_DECLARE_GTYPE(char *, G_TYPE_STRING)
|
||||
GI_DECLARE_GTYPE(std::string, G_TYPE_STRING)
|
||||
GI_DECLARE_GTYPE(gi::cstring, G_TYPE_STRING)
|
||||
GI_DECLARE_GTYPE(gi::cstring_v, G_TYPE_STRING)
|
||||
|
||||
#undef GI_DECLARE_GTYPE_VALUE
|
||||
#undef GI_DECLARE_GTYPE
|
||||
|
||||
} // namespace repository
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// GValue helpers
|
||||
|
||||
// set_value
|
||||
template<typename T,
|
||||
typename std::enable_if<traits::gvalue<T>::value>::type * = nullptr>
|
||||
inline void
|
||||
set_value(GValue *val, T v)
|
||||
{
|
||||
traits::gvalue<T>::set(val, v);
|
||||
}
|
||||
|
||||
inline void
|
||||
set_value(GValue *val, const std::string &s)
|
||||
{
|
||||
g_value_set_string(val, s.c_str());
|
||||
}
|
||||
|
||||
inline void
|
||||
set_value(GValue *val, gi::cstring_v s)
|
||||
{
|
||||
g_value_set_string(val, s.c_str());
|
||||
}
|
||||
|
||||
inline void
|
||||
set_value(GValue *val, const char *s)
|
||||
{
|
||||
g_value_set_string(val, s);
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::enable_if<traits::is_object<T>::value>::type * = nullptr>
|
||||
inline void
|
||||
set_value(GValue *val, T v)
|
||||
{
|
||||
// set might not handle NULL case
|
||||
g_value_take_object(val, v.gobj_copy_());
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::enable_if<traits::is_gboxed<T>::value>::type * = nullptr>
|
||||
inline void
|
||||
set_value(GValue *val, T v)
|
||||
{
|
||||
// set might not handle NULL case
|
||||
g_value_take_boxed(val, v.gobj_copy_());
|
||||
}
|
||||
|
||||
// container case
|
||||
template<typename T, typename T::_detail::DataType * = nullptr>
|
||||
inline void
|
||||
set_value(GValue *val, T v)
|
||||
{
|
||||
v._set_value(val);
|
||||
}
|
||||
|
||||
// get_value
|
||||
template<typename T,
|
||||
typename std::enable_if<traits::is_object<T>::value>::type * = nullptr>
|
||||
inline T
|
||||
get_value(const GValue *val)
|
||||
{
|
||||
// ensure sanity
|
||||
static_assert(std::is_class<T>::value && !std::is_const<T>::value,
|
||||
"non cv-qualified class type required");
|
||||
auto cv =
|
||||
static_cast<typename traits::ctype<T>::type>(g_value_dup_object(val));
|
||||
if (cv && !g_type_is_a(G_OBJECT_TYPE(cv), traits::gtype<T>::get_type()))
|
||||
detail::try_throw(transform_error(G_OBJECT_TYPE(cv)));
|
||||
return gi::wrap(cv, transfer_full);
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::enable_if<traits::is_gboxed<T>::value &&
|
||||
!traits::is_reftype<T>::value>::type * = nullptr>
|
||||
inline T
|
||||
get_value(const GValue *val)
|
||||
{
|
||||
// no way to know whether boxed type is correct
|
||||
// ensure sanity
|
||||
static_assert(std::is_class<T>::value && !std::is_const<T>::value,
|
||||
"non cv-qualified class type required");
|
||||
auto cv =
|
||||
static_cast<typename traits::ctype<T>::type>(g_value_dup_boxed(val));
|
||||
return gi::wrap(cv, transfer_full);
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::enable_if<traits::is_gboxed<T>::value &&
|
||||
traits::is_reftype<T>::value>::type * = nullptr>
|
||||
inline T
|
||||
get_value(const GValue *val)
|
||||
{
|
||||
// no way to know whether boxed type is correct
|
||||
// ensure sanity
|
||||
static_assert(std::is_class<T>::value && !std::is_const<T>::value,
|
||||
"non cv-qualified class type required");
|
||||
auto cv =
|
||||
static_cast<typename traits::ctype<T>::type>(g_value_get_boxed(val));
|
||||
return gi::wrap(cv, transfer_none);
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::enable_if<traits::gvalue<T>::value>::type * = nullptr>
|
||||
inline T
|
||||
get_value(const GValue *val)
|
||||
{
|
||||
return traits::gvalue<T>::get(val);
|
||||
}
|
||||
|
||||
// sigh ...
|
||||
template<typename T,
|
||||
typename std::enable_if<std::is_same<T, std::string>::value ||
|
||||
std::is_base_of<detail::String, T>::value>::type * =
|
||||
nullptr>
|
||||
inline T
|
||||
get_value(const GValue *val)
|
||||
{
|
||||
return gi::wrap(g_value_get_string(val), transfer_none);
|
||||
}
|
||||
|
||||
// container case
|
||||
template<typename T, typename T::_detail::DataType * = nullptr>
|
||||
inline T
|
||||
get_value(const GValue *val)
|
||||
{
|
||||
static_assert(traits::is_decayed<T>::value, "");
|
||||
return T::template _get_value<T>(val);
|
||||
}
|
||||
|
||||
// convenience helper ...
|
||||
template<typename T,
|
||||
typename std::enable_if<std::is_same<T, void>::value>::type * = nullptr>
|
||||
inline T
|
||||
get_value(const GValue * /*val*/)
|
||||
{}
|
||||
|
||||
// simple (RAII) Value wrapper for (internal) use
|
||||
struct Value : public GValue, noncopyable
|
||||
{
|
||||
void clear() { memset((void *)this, 0, sizeof(*this)); }
|
||||
|
||||
Value() { clear(); }
|
||||
|
||||
template<typename T, typename Enable = disable_if_same_or_derived<T, Value>>
|
||||
explicit Value(T &&v)
|
||||
{
|
||||
clear();
|
||||
g_value_init(this, traits::gtype<T>::get_type());
|
||||
set_value(this, std::forward<T>(v));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void init()
|
||||
{
|
||||
// handle no-op void (return value) corner case
|
||||
const GType tp = traits::gtype<T>::get_type();
|
||||
if (tp != G_TYPE_NONE)
|
||||
g_value_init(this, tp);
|
||||
}
|
||||
|
||||
// let's not copy, but ok to move around
|
||||
Value(Value &&other)
|
||||
{
|
||||
memcpy((void *)this, &other, sizeof(*this));
|
||||
other.clear();
|
||||
}
|
||||
|
||||
Value &operator=(Value &&other)
|
||||
{
|
||||
if (this != &other) {
|
||||
memcpy((void *)this, &other, sizeof(*this));
|
||||
other.clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Value()
|
||||
{
|
||||
if (G_VALUE_TYPE(this))
|
||||
g_value_unset(this);
|
||||
}
|
||||
};
|
||||
|
||||
// we really rely upon this as part of the ABI
|
||||
// justifies operations above and some explicit casts above
|
||||
// (to avoid -Wclass-memaccess)
|
||||
static_assert(sizeof(Value) == sizeof(GValue), "unsupported compiler");
|
||||
|
||||
template<typename R>
|
||||
inline R
|
||||
transform_value(const GValue *val)
|
||||
{
|
||||
detail::Value dest;
|
||||
dest.init<R>();
|
||||
if (!g_value_transform(val, &dest))
|
||||
detail::try_throw(detail::transform_error(G_VALUE_TYPE(&dest)));
|
||||
return detail::get_value<R>(&dest);
|
||||
}
|
||||
|
||||
// hand-crafted Value wrapper with an interface as it would be generated
|
||||
// and used by generated code, along with additional convenience
|
||||
class ValueBase : public gi::detail::GBoxedWrapperBase<ValueBase, GValue>
|
||||
{
|
||||
using self_type = ValueBase;
|
||||
|
||||
public:
|
||||
static GType get_type_() G_GNUC_CONST { return G_TYPE_VALUE; }
|
||||
|
||||
void copy(self_type dest) const { g_value_copy(gobj_(), dest.gobj_()); }
|
||||
|
||||
void reset() { g_value_reset(gobj_()); }
|
||||
|
||||
void unset() { g_value_unset(gobj_()); }
|
||||
|
||||
bool transform(self_type dest) const
|
||||
{
|
||||
return g_value_transform(gobj_(), dest.gobj_());
|
||||
}
|
||||
|
||||
static bool type_compatible(GType src_type, GType dest_type)
|
||||
{
|
||||
return g_value_type_compatible(src_type, dest_type);
|
||||
}
|
||||
|
||||
static bool type_transformable(GType src_type, GType dest_type)
|
||||
{
|
||||
return g_value_type_transformable(src_type, dest_type);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
self_type &set_value(T &&v)
|
||||
{
|
||||
detail::set_value(gobj_(), std::forward<T>(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get_value() const
|
||||
{
|
||||
return detail::get_value<T>(gobj_());
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
R transform_value()
|
||||
{
|
||||
return detail::transform_value<R>(gobj_());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace repository
|
||||
{
|
||||
namespace GObject
|
||||
{
|
||||
// build on above base with additional convenience (in owning case)
|
||||
class Value_Ref;
|
||||
class Value : public gi::detail::GBoxedWrapper<Value, GValue, detail::ValueBase,
|
||||
Value_Ref>
|
||||
{
|
||||
typedef gi::detail::GBoxedWrapper<Value, GValue, detail::ValueBase, Value_Ref>
|
||||
super_type;
|
||||
typedef Value self_type;
|
||||
|
||||
public:
|
||||
using detail::ValueBase::copy;
|
||||
using super_type::copy;
|
||||
|
||||
// hybrid GBoxed/CBoxed
|
||||
void allocate_()
|
||||
{
|
||||
if (this->data_)
|
||||
return;
|
||||
// make sure we match GValue boxed allocation with boxed free
|
||||
// (though last kown implementation uses g_new0/g_free)
|
||||
detail::Value tmp;
|
||||
this->data_ = (::GValue *)g_boxed_copy(G_TYPE_VALUE, &tmp);
|
||||
}
|
||||
|
||||
// convenience
|
||||
Value() { allocate_(); }
|
||||
|
||||
// allow non-explicit use for convenient calling
|
||||
// but avoid copy/move construct use
|
||||
template<typename T,
|
||||
typename std::enable_if<!std::is_base_of<Value,
|
||||
typename std::remove_reference<T>::type>::value>::type * = nullptr>
|
||||
Value(T &&t)
|
||||
{
|
||||
allocate_();
|
||||
init<T>(std::forward<T>(t));
|
||||
}
|
||||
|
||||
Value &init(GType tp)
|
||||
{
|
||||
// no-op void corner case
|
||||
if (tp != G_TYPE_NONE)
|
||||
g_value_init(gobj_(), tp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Value &init(T &&v)
|
||||
{
|
||||
g_value_init(gobj_(), traits::gtype<T>::get_type());
|
||||
set_value(std::forward<T>(v));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class Value_Ref
|
||||
: public gi::detail::GBoxedRefWrapper<Value, ::GValue, detail::ValueBase>
|
||||
{
|
||||
typedef gi::detail::GBoxedRefWrapper<Value, ::GValue, detail::ValueBase>
|
||||
super_type;
|
||||
using super_type::super_type;
|
||||
};
|
||||
|
||||
} // namespace GObject
|
||||
|
||||
template<>
|
||||
struct declare_cpptype_of<GValue>
|
||||
{
|
||||
typedef GObject::Value type;
|
||||
};
|
||||
|
||||
} // namespace repository
|
||||
|
||||
} // namespace gi
|
||||
|
||||
#endif // GI_VALUE_HPP
|
||||
369
cmake/external/glib/cppgir/gi/wrap.hpp
vendored
Normal file
369
cmake/external/glib/cppgir/gi/wrap.hpp
vendored
Normal file
@@ -0,0 +1,369 @@
|
||||
#ifndef GI_WRAP_HPP
|
||||
#define GI_WRAP_HPP
|
||||
|
||||
#include "base.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
GI_MODULE_EXPORT
|
||||
namespace gi
|
||||
{
|
||||
// object/wrapper conversion
|
||||
template<typename CType, typename TransferType,
|
||||
typename CppType = typename traits::cpptype<CType *>::type,
|
||||
typename Enable =
|
||||
typename std::enable_if<traits::is_wrapper<CppType>::value>::type>
|
||||
inline typename std::remove_const<CppType>::type
|
||||
wrap(CType *v, const TransferType &t)
|
||||
{
|
||||
// should be called with a concrete transfer subtype
|
||||
static_assert(!std::is_same<TransferType, transfer_t>::value, "");
|
||||
// the class wrap only has to deal with non-const class type
|
||||
typedef typename std::remove_const<CppType>::type TNC;
|
||||
return CppType::template wrap<TNC>(v, t.value);
|
||||
}
|
||||
|
||||
// special case; wrap an owned box with full transfer
|
||||
template<typename CType,
|
||||
typename CppType = typename traits::cpptype<CType *>::type,
|
||||
typename Enable =
|
||||
typename std::enable_if<traits::is_boxed<CppType>::value>::type,
|
||||
typename TNC = typename std::remove_const<CppType>::type>
|
||||
inline TNC
|
||||
wrap(CType *v, const transfer_full_t &)
|
||||
{
|
||||
return CppType::template wrap<TNC>(v);
|
||||
}
|
||||
|
||||
// special case; wrap a unowned box (that is, transfer none) to the Ref type
|
||||
template<typename CType,
|
||||
typename CppType = typename traits::cpptype<CType *>::type,
|
||||
typename Enable =
|
||||
typename std::enable_if<traits::is_boxed<CppType>::value>::type,
|
||||
typename RefType = typename traits::reftype<
|
||||
typename std::remove_const<CppType>::type>::type>
|
||||
inline RefType
|
||||
wrap(CType *v, const transfer_none_t &)
|
||||
{
|
||||
// unowned and no copy in all cases
|
||||
return RefType::template wrap<RefType>(v);
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::remove_reference<T>::type::BaseObjectType * = nullptr>
|
||||
inline typename traits::ctype<T>::type
|
||||
unwrap(T &&v, const transfer_none_t &)
|
||||
{
|
||||
using DT = typename std::decay<T>::type;
|
||||
// test convenience
|
||||
#ifndef GI_TEST
|
||||
static constexpr bool ALLOW_ALL = false;
|
||||
#else
|
||||
static constexpr bool ALLOW_ALL = true;
|
||||
#endif
|
||||
static_assert(ALLOW_ALL || traits::is_wrapper<DT>::value ||
|
||||
traits::is_reftype<DT>::value,
|
||||
"transfer none expects refcnt wrapper or reftype (not owning box)");
|
||||
return v.gobj_();
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// lvalue
|
||||
template<typename T,
|
||||
typename std::enable_if<!traits::is_boxed<T>::value>::type * = nullptr>
|
||||
inline typename traits::ctype<T>::type
|
||||
unwrap(const T &v, const transfer_full_t &, std::true_type)
|
||||
{
|
||||
// no implicit copy for boxed; should end up in other case
|
||||
static_assert(!traits::is_boxed<T>::value, "boxed copy");
|
||||
return v.gobj_copy_();
|
||||
}
|
||||
|
||||
// rvalue
|
||||
template<typename T,
|
||||
typename std::enable_if<!traits::is_reftype<T>::value>::type * = nullptr>
|
||||
inline typename traits::ctype<T>::type
|
||||
unwrap(T &&v, const transfer_full_t &, std::false_type)
|
||||
{
|
||||
// in case of wrapper/object;
|
||||
// release only provided on base case with void* return
|
||||
return (typename traits::ctype<T>::type)v.release_();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, typename std::decay<T>::type::BaseObjectType * = nullptr>
|
||||
inline typename traits::ctype<T>::type
|
||||
unwrap(T &&v, const transfer_full_t &t)
|
||||
{
|
||||
// universal reference dispatch
|
||||
return detail::unwrap(std::forward<T>(v), t, std::is_lvalue_reference<T>());
|
||||
}
|
||||
|
||||
// container types
|
||||
template<typename T, typename Transfer,
|
||||
typename std::decay<T>::type::_detail::DataType * = nullptr>
|
||||
inline typename std::decay<T>::type::_detail::DataType
|
||||
unwrap(T &&v, const Transfer &t)
|
||||
{
|
||||
// universal reference dispatch
|
||||
return std::forward<T>(v)._unwrap(t);
|
||||
}
|
||||
|
||||
// to wrap a container, the target wrapped type needs to be explicitly specified
|
||||
// (in particular the contained element type)
|
||||
// (target type should be decay'ed type)
|
||||
|
||||
// generic case, let wrap take care of it (usually no target type is needed)
|
||||
template<typename TargetType, typename CType, typename Transfer,
|
||||
decltype(wrap(std::declval<typename std::decay<CType>::type>(),
|
||||
Transfer())) * = nullptr>
|
||||
TargetType
|
||||
wrap_to(CType v, const Transfer &t)
|
||||
{
|
||||
static_assert(traits::is_decayed<TargetType>::value, "");
|
||||
return wrap(v, t);
|
||||
}
|
||||
|
||||
// container case
|
||||
template<typename TargetType, typename CType, typename Transfer,
|
||||
typename TargetType::_detail::DataType * = nullptr>
|
||||
TargetType
|
||||
wrap_to(CType v, const Transfer &t)
|
||||
{
|
||||
static_assert(traits::is_decayed<TargetType>::value, "");
|
||||
return TargetType::template _wrap<TargetType>(v, t);
|
||||
}
|
||||
|
||||
// container size case
|
||||
template<typename TargetType, typename CType, typename Transfer,
|
||||
typename TargetType::_detail::DataType * = nullptr>
|
||||
TargetType
|
||||
wrap_to(CType v, int s, const Transfer &t)
|
||||
{
|
||||
static_assert(traits::is_decayed<TargetType>::value, "");
|
||||
return TargetType::template _wrap<TargetType>(v, s, t);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// string conversion
|
||||
inline std::string
|
||||
wrap(const char *v, const transfer_none_t &,
|
||||
const direction_t & = direction_dummy)
|
||||
{
|
||||
return detail::make_string(v);
|
||||
}
|
||||
|
||||
// actually should not accept const input (as it makes no sense for full
|
||||
// transfer) but let's go the runtime way and not mind that too much (code
|
||||
// generation will warn though)
|
||||
inline std::string
|
||||
wrap(const char *v, const transfer_full_t &,
|
||||
const direction_t & = direction_dummy)
|
||||
{
|
||||
// a custom type that would allow direct mem transfer might be nice
|
||||
// but that might be too nifty and create yet-another-string-type
|
||||
std::string s;
|
||||
if (v) {
|
||||
s = v;
|
||||
g_free((char *)v);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
#else
|
||||
// string conversion
|
||||
inline gi::cstring_v
|
||||
wrap(const char *v, const transfer_none_t &)
|
||||
{
|
||||
return cstring_v(v);
|
||||
}
|
||||
|
||||
// actually should not accept const input (as it makes no sense for full
|
||||
// transfer) but let's go the runtime way and not mind that too much (code
|
||||
// generation will warn though)
|
||||
inline gi::cstring
|
||||
wrap(const char *v, const transfer_full_t &)
|
||||
{
|
||||
// as said, never mind const
|
||||
return cstring{(char *)v, transfer_full};
|
||||
}
|
||||
#endif
|
||||
|
||||
// return const here, as somewhat customary, also
|
||||
// wrapped function call is force-casted anyway (to const char* parameter)
|
||||
// FIXME ?? though const is generally rare and it breaks consistency that way
|
||||
inline const gchar *
|
||||
unwrap(const std::string &v, const transfer_none_t &)
|
||||
{
|
||||
return v.c_str();
|
||||
}
|
||||
|
||||
inline const gchar *
|
||||
unwrap(const detail::optional_string &v, const transfer_none_t &)
|
||||
{
|
||||
return v.empty() ? nullptr : v.c_str();
|
||||
}
|
||||
|
||||
template<typename Transfer>
|
||||
inline const gchar *
|
||||
unwrap(const detail::cstr<Transfer> &v, const transfer_none_t &)
|
||||
{
|
||||
return v.c_str();
|
||||
}
|
||||
|
||||
// R-value variants of the above, akin to transfer_none from an owning R-value
|
||||
// bad dangling things would happen
|
||||
inline const gchar *unwrap(std::string &&v, const transfer_none_t &) = delete;
|
||||
|
||||
inline const gchar *unwrap(
|
||||
detail::optional_string &&v, const transfer_none_t &) = delete;
|
||||
|
||||
template<typename Transfer>
|
||||
inline const gchar *
|
||||
unwrap(detail::cstr<Transfer> &&v, const transfer_none_t &)
|
||||
{
|
||||
static_assert(std::is_same<Transfer, transfer_none_t>::value,
|
||||
"transfer none expects non-owning type");
|
||||
return v.c_str();
|
||||
}
|
||||
|
||||
inline gchar *
|
||||
unwrap(const std::string &v, const transfer_full_t &)
|
||||
{
|
||||
return g_strdup(v.c_str());
|
||||
}
|
||||
|
||||
inline gchar *
|
||||
unwrap(const detail::optional_string &v, const transfer_full_t &)
|
||||
{
|
||||
return v.empty() ? nullptr : g_strdup(v.c_str());
|
||||
}
|
||||
|
||||
template<typename Transfer>
|
||||
inline gchar *
|
||||
unwrap(const gi::detail::cstr<Transfer> &v, const transfer_full_t &)
|
||||
{
|
||||
return g_strdup(v.c_str());
|
||||
}
|
||||
|
||||
inline gchar *
|
||||
unwrap(gi::cstring &&v, const transfer_full_t &)
|
||||
{
|
||||
return v.release_();
|
||||
}
|
||||
|
||||
// enum conversion
|
||||
template<typename T,
|
||||
typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
|
||||
inline typename traits::cpptype<T>::type
|
||||
wrap(T v, const transfer_t & = transfer_dummy)
|
||||
{
|
||||
return (typename traits::cpptype<T>::type)v;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
|
||||
inline typename traits::ctype<T>::type
|
||||
unwrap(T v, const transfer_t & = transfer_dummy)
|
||||
{
|
||||
return (typename traits::ctype<T>::type)v;
|
||||
}
|
||||
|
||||
// plain basic pass along
|
||||
template<typename T,
|
||||
typename std::enable_if<traits::is_basic<T>::value>::type * = nullptr>
|
||||
inline T
|
||||
wrap(T v, const transfer_t & = transfer_dummy)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename std::enable_if<traits::is_basic<T>::value>::type * = nullptr>
|
||||
inline T
|
||||
unwrap(T v, const transfer_t & = transfer_dummy)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
// callback conversion
|
||||
// async or destroy-notify;
|
||||
// signature forces copy, and std::move is used in unwrap call
|
||||
template<typename T,
|
||||
typename std::remove_reference<T>::type::CallbackWrapperType * = nullptr>
|
||||
inline typename std::remove_reference<T>::type::CallbackWrapperType
|
||||
unwrap(T &&v, const transfer_t & = transfer_dummy)
|
||||
{
|
||||
return typename std::remove_reference<T>::type::CallbackWrapperType(
|
||||
std::forward<T>(v));
|
||||
}
|
||||
|
||||
// call or destroy-notify scope
|
||||
template<typename T>
|
||||
inline typename std::remove_reference<T>::type::template wrapper_type<false> *
|
||||
unwrap(T &&v, const scope_t &)
|
||||
{
|
||||
return new
|
||||
typename std::remove_reference<T>::type::template wrapper_type<false>(
|
||||
std::forward<T>(v));
|
||||
}
|
||||
|
||||
// async scope
|
||||
template<typename T>
|
||||
inline typename std::remove_reference<T>::type::template wrapper_type<true> *
|
||||
unwrap(T &&v, const scope_async_t &)
|
||||
{
|
||||
return new
|
||||
typename std::remove_reference<T>::type::template wrapper_type<true>(
|
||||
std::forward<T>(v));
|
||||
}
|
||||
|
||||
// dynamic GType casting within GObject/interface hierarchy
|
||||
template<typename T, typename I,
|
||||
typename std::enable_if<
|
||||
traits::is_object<T>::value &&
|
||||
traits::is_object<typename std::decay<I>::type>::value>::type * =
|
||||
nullptr>
|
||||
inline T
|
||||
object_cast(I &&t)
|
||||
{
|
||||
if (!t || !g_type_is_a(t.gobj_type_(), T::get_type_())) {
|
||||
return T();
|
||||
} else {
|
||||
return wrap((typename T::BaseObjectType *)t.gobj_copy_(), transfer_full);
|
||||
}
|
||||
}
|
||||
|
||||
// this utility can be used to arrange for pointer-like const-ness
|
||||
// that is, a const shared_ptr<T> is still usable like (non-const) T*
|
||||
// in a way, any wrapper object T acts much like a smart-pointer,
|
||||
// but as the (code generated) methods are non-const, they are not usable
|
||||
// if the wrapper object is const (e.g. captured in a lambda)
|
||||
// this helper object/class can be wrapped around the wrapper (phew)
|
||||
// to absorb/shield the (outer) `const` (as it behaves as other smart pointers)
|
||||
template<typename T>
|
||||
class cs_ptr
|
||||
{
|
||||
T t;
|
||||
|
||||
public:
|
||||
// rough check; T is expected to be a pointer wrapper
|
||||
static_assert(sizeof(T) == sizeof(void *), "");
|
||||
|
||||
// if T not copy-able, argument may need to be move'd
|
||||
cs_ptr(T _t) : t(std::move(_t)) {}
|
||||
|
||||
operator T() const & { return t; }
|
||||
operator T() && { return std::move(t); }
|
||||
|
||||
T *get() const { return &t; }
|
||||
|
||||
// C++ sacrilege,
|
||||
// but the const of pointer in T does not extend to pointee anyway
|
||||
T *operator*() const { return const_cast<T *>(&t); }
|
||||
T *operator->() const { return const_cast<T *>(&t); }
|
||||
};
|
||||
|
||||
} // namespace gi
|
||||
|
||||
#endif // GI_WRAP_HPP
|
||||
194
cmake/external/glib/cppgir/meson.build
vendored
Normal file
194
cmake/external/glib/cppgir/meson.build
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
project('cppgir',
|
||||
['c', 'cpp'],
|
||||
meson_version : '>= 0.61',
|
||||
version : '2.0.0',
|
||||
default_options : [
|
||||
'warning_level=2',
|
||||
'cpp_std=c++17'
|
||||
]
|
||||
)
|
||||
|
||||
message('meson system only considers generator and includes',
|
||||
'\n\tsee CMake build for full build including examples')
|
||||
|
||||
compiler = meson.get_compiler('cpp')
|
||||
|
||||
foreach arg : ['-Wnon-virtual-dtor']
|
||||
if compiler.has_argument(arg)
|
||||
add_project_arguments(arg, language: 'cpp')
|
||||
endif
|
||||
endforeach
|
||||
|
||||
# generator binary
|
||||
|
||||
# dependencies
|
||||
boost_dep = dependency('boost', version : '>=1.58', required : true)
|
||||
|
||||
# fmtlib
|
||||
fmtlib_dep = dependency('fmt', required : false)
|
||||
if not fmtlib_dep.found()
|
||||
# fallback for old version without pkg-config
|
||||
fmtlib_dep = compiler.find_library('fmt', has_headers : ['fmt/format.h'], required : false)
|
||||
endif
|
||||
# check C++20 format
|
||||
has_format = compiler.compiles(files('cmake/cpp20_format.cpp'),
|
||||
args : ['-std=c++20'],
|
||||
name : 'std::format check'
|
||||
)
|
||||
|
||||
# check and decide on which fmt to use
|
||||
use_fmtlib = 'none'
|
||||
build_fmt = get_option('build_fmt')
|
||||
if build_fmt == 'auto'
|
||||
if fmtlib_dep.found()
|
||||
use_fmtlib = '1'
|
||||
elif has_format
|
||||
use_fmtlib = '0'
|
||||
endif
|
||||
elif build_fmt == 'fmtlib' and fmtlib_dep.found()
|
||||
use_fmtlib = '1'
|
||||
elif build_fmt == 'stdformat' and has_format
|
||||
use_fmtlib = '0'
|
||||
endif
|
||||
if use_fmtlib == 'none'
|
||||
error('no format library found')
|
||||
endif
|
||||
|
||||
# required ignore file
|
||||
fs = import('fs')
|
||||
|
||||
gi_ignore_file_dir = 'data'
|
||||
gi_ignore_file = 'cppgir.ignore'
|
||||
cppgir_ignore = fs.read('data/cppgir.ignore')
|
||||
if host_machine.system() == 'windows'
|
||||
gi_ignore_file_platform = 'cppgir_win.ignore'
|
||||
cppgir_win_ignore = fs.read('data/cppgir_win.ignore')
|
||||
cppgir_unix_ignore = ''
|
||||
else
|
||||
gi_ignore_file_platform = 'cppgir_unix.ignore'
|
||||
cppgir_unix_ignore = fs.read('data/cppgir_unix.ignore')
|
||||
cppgir_win_ignore = ''
|
||||
endif
|
||||
gi_install_full_datadir = \
|
||||
'@0@/@1@'.format(get_option('prefix'), get_option('datadir'))
|
||||
gi_ignore_file_install_dir = \
|
||||
'@0@/@1@'.format(gi_install_full_datadir, meson.project_name())
|
||||
|
||||
cppgir_sources = [
|
||||
'tools/cppgir.cpp',
|
||||
'tools/genbase.cpp', 'tools/genbase.hpp',
|
||||
'tools/genns.cpp', 'tools/genns.hpp',
|
||||
'tools/genutils.cpp', 'tools/genutils.hpp',
|
||||
'tools/function.cpp', 'tools/function.hpp',
|
||||
'tools/repository.cpp', 'tools/repository.hpp',
|
||||
'tools/common.hpp'
|
||||
]
|
||||
|
||||
cppgir_deps = [boost_dep]
|
||||
cppgir_overrides = []
|
||||
cppgir_args = []
|
||||
|
||||
# adjust to options and situation
|
||||
if use_fmtlib.to_int() > 0
|
||||
cppgir_deps += [fmtlib_dep]
|
||||
else
|
||||
cppgir_overrides = ['cpp_std=c++20']
|
||||
endif
|
||||
|
||||
if get_option('build_embed_ignore')
|
||||
# generate embedded ignore data
|
||||
conf_data = configuration_data()
|
||||
conf_data.set('CPPGIR_IGNORE', cppgir_ignore)
|
||||
conf_data.set('CPPGIR_UNIX_IGNORE', cppgir_unix_ignore)
|
||||
conf_data.set('CPPGIR_WIN_IGNORE', cppgir_win_ignore)
|
||||
configure_file(configuration : conf_data,
|
||||
input : 'tools/ignore.hpp.in', output : 'ignore.hpp')
|
||||
else
|
||||
install_data(gi_ignore_file_dir / gi_ignore_file,
|
||||
gi_ignore_file_dir / gi_ignore_file_platform,
|
||||
install_dir : gi_ignore_file_install_dir)
|
||||
cppgir_args += \
|
||||
[f'-DDEFAULT_IGNORE_FILE=@gi_ignore_file_install_dir@/@gi_ignore_file@:@gi_ignore_file_install_dir@/@gi_ignore_file_platform@']
|
||||
endif
|
||||
|
||||
# gir search path
|
||||
if host_machine.system() != 'windows'
|
||||
# add fixed fallback search places
|
||||
cppgir_args += [
|
||||
f'-DGI_DATA_DIR=@gi_install_full_datadir@/gir-1.0',
|
||||
]
|
||||
gir_dir = get_option('gir_dir')
|
||||
if gir_dir != ''
|
||||
cppgir_args += [f'-DGI_GIR_DIR=@gir_dir@']
|
||||
endif
|
||||
gir_default_dirs = get_option('gir_default_dirs')
|
||||
if gir_default_dirs != ''
|
||||
cppgir_args += [f'-DDEFAULT_GIRPATH=@gir_default_dirs@']
|
||||
endif
|
||||
endif
|
||||
|
||||
if get_option('link_stdfs')
|
||||
# some older gcc might sometimes (?) need this, even in c++17 mode
|
||||
# see issue #80
|
||||
fs_dep = compiler.find_library('stdc++fs')
|
||||
cppgir_deps += [fs_dep]
|
||||
endif
|
||||
|
||||
cppgir = executable('cppgir',
|
||||
cppgir_sources,
|
||||
cpp_args : cppgir_args,
|
||||
dependencies : cppgir_deps,
|
||||
install : true,
|
||||
override_options : cppgir_overrides
|
||||
)
|
||||
meson.override_find_program('cppgir', cppgir)
|
||||
|
||||
# gi headers
|
||||
expected_code = '''#include <expected>
|
||||
auto f() -> std::expected<int, int> { return 2; }
|
||||
'''
|
||||
has_expected = compiler.compiles(expected_code, name : 'std::expected check')
|
||||
|
||||
pkgconfig = import('pkgconfig')
|
||||
|
||||
pkgconfig.generate(name: 'cppgir',
|
||||
version : meson.project_version(),
|
||||
subdirs : ['cppgir', 'cppgir' / 'override'],
|
||||
description : 'GObject Introspection C++ wrapper generator.'
|
||||
)
|
||||
|
||||
install_subdir('gi', install_dir : 'include' / 'cppgir')
|
||||
install_subdir('override', install_dir : 'include' / 'cppgir')
|
||||
|
||||
inc = include_directories('.', 'override')
|
||||
if not has_expected
|
||||
expected_lite_include = 'expected-lite' / 'include'
|
||||
if not fs.exists(expected_lite_include / 'nonstd' / 'expected.hpp')
|
||||
error('missing submodule expected-lite')
|
||||
endif
|
||||
# Add an include option and copy the directory is all we have to do.
|
||||
# cppgir will automatically switch to `nonstd/expected.hpp` if it exists.
|
||||
inc = [inc] + include_directories(expected_lite_include)
|
||||
install_subdir(expected_lite_include / 'nonstd',
|
||||
install_dir : 'include' / 'cppgir' / 'gi')
|
||||
endif
|
||||
|
||||
cppgir_dep = declare_dependency(include_directories: inc)
|
||||
meson.override_dependency('cppgir', cppgir_dep)
|
||||
|
||||
# manpage
|
||||
ronn = find_program('ronn', native : true, required : false)
|
||||
if not ronn.found()
|
||||
message('ronn manpage processor not found; not building manpage')
|
||||
elif get_option('build_doc')
|
||||
message('building manpage')
|
||||
manpage = 'cppgir.1'
|
||||
custom_target(manpage, command : [ronn, '--roff', '--pipe', '@INPUT@'],
|
||||
capture : true, input : ['docs/cppgir.md'], output : manpage,
|
||||
install : true, install_dir : join_paths(get_option('mandir'), 'man1'))
|
||||
endif
|
||||
|
||||
# documentation, including examples
|
||||
docdir = get_option('datadir') / 'doc' / 'cppgir'
|
||||
install_data('README.md', 'docs/cppgir.md', install_dir : docdir)
|
||||
install_subdir('examples', install_dir : docdir)
|
||||
31
cmake/external/glib/cppgir/meson_options.txt
vendored
Normal file
31
cmake/external/glib/cppgir/meson_options.txt
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# Build option
|
||||
option('build_fmt', type : 'combo',
|
||||
choices : ['auto', 'fmtlib', 'stdformat'] ,
|
||||
value : 'auto',
|
||||
description : 'format library'
|
||||
)
|
||||
|
||||
option('build_embed_ignore', type : 'boolean',
|
||||
value : false,
|
||||
description : 'embed default ignore'
|
||||
)
|
||||
|
||||
option('gir_dir', type : 'string',
|
||||
description : 'extra GIR search directory'
|
||||
)
|
||||
|
||||
option('gir_default_dirs', type : 'string',
|
||||
description : 'fallback GIR search prefix paths (to be suffixed with gir-1.0)',
|
||||
# best left as-is to follow standard GIR search
|
||||
value : '/usr/local/share:/usr/share'
|
||||
)
|
||||
|
||||
option('link_stdfs', type : 'boolean',
|
||||
value : false,
|
||||
description : 'link to stdc++fs'
|
||||
)
|
||||
|
||||
option('build_doc', type : 'boolean',
|
||||
value : true,
|
||||
description : 'build documentation'
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user