Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support C++20 module with optional standard library module. #61

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ option(FASTGLTF_ENABLE_DEPRECATED_EXT "Enables support for deprecated extensions
option(FASTGLTF_DISABLE_CUSTOM_MEMORY_POOL "Disables the memory allocation algorithm based on polymorphic resources" OFF)
option(FASTGLTF_USE_64BIT_FLOAT "Default to 64-bit double precision floats for everything" OFF)
option(FASTGLTF_COMPILE_AS_CPP20 "Have the library compile as C++20" OFF)
option(FASTGLTF_USE_STD_MODULE "Use the std module when compiling using C++ modules" OFF)

if (FASTGLTF_COMPILE_AS_CPP20)
set(FASTGLTF_COMPILE_TARGET cxx_std_20)
Expand All @@ -26,7 +27,7 @@ else()
endif()

include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/add_source_directory.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/compiler_flags.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/compilers.cmake)

# Create the library target
add_library(fastgltf
Expand All @@ -42,6 +43,10 @@ target_include_directories(fastgltf PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_LIS
set_target_properties(fastgltf PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS YES)
set_target_properties(fastgltf PROPERTIES VERSION ${PROJECT_VERSION})

if (ANDROID)
target_link_libraries(fastgltf PRIVATE android)
endif()

# If the target already exists due to the parent script already including it as a dependency, just directly link it.
if (TARGET simdjson::simdjson)
target_link_libraries(fastgltf PRIVATE simdjson::simdjson)
Expand Down Expand Up @@ -102,8 +107,31 @@ target_compile_definitions(fastgltf PUBLIC "FASTGLTF_ENABLE_DEPRECATED_EXT=$<BOO
target_compile_definitions(fastgltf PUBLIC "FASTGLTF_DISABLE_CUSTOM_MEMORY_POOL=$<BOOL:${FASTGLTF_DISABLE_CUSTOM_MEMORY_POOL}>")
target_compile_definitions(fastgltf PUBLIC "FASTGLTF_USE_64BIT_FLOAT=$<BOOL:${FASTGLTF_USE_64BIT_FLOAT}>")

if (ANDROID)
target_link_libraries(fastgltf PRIVATE android)
fastgltf_check_modules_support()
if (FASTGLTF_SUPPORTS_MODULES AND CMAKE_VERSION VERSION_GREATER_EQUAL "3.28")
message(STATUS "fastgltf: Found compiler support for CXX modules")

# 3.29.20240416 is what the CMake blog used for talking about import std, so this should roughly be the first
# version to support the feature.
if (FASTGLTF_USE_STD_MODULE AND CMAKE_VERSION VERSION_LESS "3.29.20240416")
message(AUTHOR_WARNING "fastgltf: Using the std module is only natively supported with CMake 3.30 or newer. This might cause compilation errors.")
endif()

add_library(fastgltf_module)
add_library(fastgltf::module ALIAS fastgltf_module)

if (FASTGLTF_USE_STD_MODULE)
target_compile_features(fastgltf_module PRIVATE cxx_std_23 INTERFACE cxx_std_20)
else()
target_compile_features(fastgltf_module PUBLIC cxx_std_20)
endif()
target_sources(fastgltf_module PUBLIC
FILE_SET CXX_MODULES
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src
FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/fastgltf.ixx
)
target_link_libraries(fastgltf_module PRIVATE fastgltf::fastgltf)
target_compile_definitions(fastgltf_module PUBLIC "FASTGLTF_USE_STD_MODULE=$<BOOL:${FASTGLTF_USE_STD_MODULE}>")
endif()

install(
Expand Down
9 changes: 9 additions & 0 deletions cmake/compiler_flags.cmake → cmake/compilers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,12 @@ macro(fastgltf_enable_debug_inlining TARGET)
endif()
endif()
endmacro()

function(fastgltf_check_modules_support)
# Clang 16 and newer support modules, as well as Visual Studio 17.1 (1931) and newer.
if ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "16.0.0") OR (MSVC AND MSVC_VERSION GREATER_EQUAL 1931) OR (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "14.1.0"))
set(FASTGLTF_SUPPORTS_MODULES true PARENT_SCOPE)
else()
set(FASTGLTF_SUPPORTS_MODULES false PARENT_SCOPE)
endif()
endfunction()
26 changes: 19 additions & 7 deletions docs/options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ Options
CMake options
=============

All of the following CMake options simply define the corresponding macro for compilation.
Therefore, when using another build system these compile definitions can simply be set to 0 or 1,
and the fastgltf headers also define a fallback if undefined.
However, you should not define any of those macros within your source files, since that would break compilation and/or linking.

``FASTGLTF_USE_64BIT_FLOAT``
----------------------------

Expand Down Expand Up @@ -35,6 +40,20 @@ This allocator allocates fixed-size blocks of memory as needed and divides them
All of this functionality can be disabled using this flag.
All types will then be normal ``std`` containers and use standard heap allocation with new and malloc.

``FASTGLTF_COMPILE_AS_CPP20``
-----------------------------

This ``BOOL`` option controls the C++ standard the library is compiled as. When ``NO`` fastgltf is always compiled as C++17.
When ``YES`` fastgltf is compiled as C++20, including the tests. This might allow the compiler to perform certain optimisations,
since fastgltf then uses some specialized stdlib functions instead.

``FASTGLTF_USE_STD_MODULE``
---------------------------

This ``BOOL`` option makes the fastgltf C++ module import the std module, which is available since C++23.
This can have further compiler time benefits, but is only experimentally supported with CMake 3.30 or newer, and only with some toolchains.
Therefore, this option should be used with caution and issues are likely.


``FASTGLTF_ENABLE_TESTS``
-------------------------
Expand Down Expand Up @@ -73,13 +92,6 @@ When this ``BOOL`` option is set to ``YES`` fastgltf will use `corrosion`_, whic
to link against the `gltf-rs`_ Rust library for comparison within the benchmarks.
Note that this option has no effect when ``FASTGLTF_ENABLE_TESTS`` is set to ``NO``.

``FASTGLTF_COMPILE_AS_CPP20``
-----------------------------

This ``BOOL`` option controls the C++ standard the library is compiled as. When ``NO`` fastgltf is always compiled as C++17.
When ``YES`` fastgltf is compiled as C++20, including the tests.


Parsing options
===============

Expand Down
10 changes: 6 additions & 4 deletions include/fastgltf/base64.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@

#pragma once

#if !defined(FASTGLTF_USE_STD_MODULE) || !FASTGLTF_USE_STD_MODULE
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <string_view>
#endif

#include <fastgltf/types.hpp>

Expand All @@ -45,7 +47,7 @@ namespace fastgltf::base64 {
* points to the original string that has a size that is a multiple of 4 and is at least
* 4 chars long.
*/
[[gnu::always_inline]] constexpr std::size_t getPadding(std::string_view string) {
FASTGLTF_EXPORT [[gnu::always_inline]] constexpr std::size_t getPadding(std::string_view string) {
assert(string.size() >= 4 && string.size() % 4 == 0);
const auto size = string.size();
for (auto i = 1; i < 4; ++i)
Expand All @@ -58,7 +60,7 @@ namespace fastgltf::base64 {
* Calculates the size of the decoded string based on the size of the base64 encoded string and
* the amount of padding the encoded data contains.
*/
[[gnu::always_inline]] constexpr std::size_t getOutputSize(std::size_t encodedSize, std::size_t padding) noexcept {
FASTGLTF_EXPORT [[gnu::always_inline]] constexpr std::size_t getOutputSize(std::size_t encodedSize, std::size_t padding) noexcept {
assert(encodedSize % 4 == 0);
return (encodedSize / 4) * 3 - padding;
}
Expand All @@ -74,10 +76,10 @@ namespace fastgltf::base64 {
[[nodiscard]] StaticVector<std::uint8_t> neon_decode(std::string_view encoded);
#endif
void fallback_decode_inplace(std::string_view encoded, std::uint8_t* output, std::size_t padding);
void decode_inplace(std::string_view encoded, std::uint8_t* output, std::size_t padding);
FASTGLTF_EXPORT void decode_inplace(std::string_view encoded, std::uint8_t* output, std::size_t padding);

[[nodiscard]] StaticVector<std::uint8_t> fallback_decode(std::string_view encoded);
[[nodiscard]] StaticVector<std::uint8_t> decode(std::string_view encoded);
FASTGLTF_EXPORT [[nodiscard]] StaticVector<std::uint8_t> decode(std::string_view encoded);
} // namespace fastgltf::base64

#ifdef _MSC_VER
Expand Down
57 changes: 30 additions & 27 deletions include/fastgltf/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@

#pragma once

#if !defined(FASTGLTF_USE_STD_MODULE) || !FASTGLTF_USE_STD_MODULE
#include <fstream>
#include <memory>
#include <tuple>
#endif

#include <fastgltf/types.hpp>

Expand All @@ -50,9 +52,9 @@ namespace simdjson::dom {
} // namespace simdjson::dom

namespace fastgltf {
enum class Error : std::uint64_t;
FASTGLTF_EXPORT enum class Error : std::uint64_t;

template <typename T>
FASTGLTF_EXPORT template <typename T>
class Expected;
} // namespace fastgltf

Expand All @@ -68,7 +70,7 @@ namespace std {

namespace fastgltf {
struct BinaryGltfChunk;
class GltfDataGetter;
FASTGLTF_EXPORT class GltfDataGetter;

enum class Error : std::uint64_t {
None = 0,
Expand All @@ -92,7 +94,7 @@ namespace fastgltf {
FileBufferAllocationFailed = 14, ///< The constructor of GltfDataBuffer failed to allocate a sufficiently large buffer.
};

inline std::string_view getErrorName(Error error) {
FASTGLTF_EXPORT constexpr std::string_view getErrorName(Error error) {
switch (error) {
case Error::None: return "None";
case Error::InvalidPath: return "InvalidPath";
Expand All @@ -113,7 +115,7 @@ namespace fastgltf {
}
}

inline std::string_view getErrorMessage(Error error) {
FASTGLTF_EXPORT constexpr std::string_view getErrorMessage(Error error) {
switch (error) {
case Error::None: return "";
case Error::InvalidPath: return "The glTF directory passed to load*GLTF is invalid";
Expand All @@ -135,7 +137,7 @@ namespace fastgltf {
}

// clang-format off
enum class Extensions : std::uint64_t {
FASTGLTF_EXPORT enum class Extensions : std::uint64_t {
None = 0,

// See https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_texture_transform/README.md
Expand Down Expand Up @@ -218,13 +220,13 @@ namespace fastgltf {
FASTGLTF_ASSIGNMENT_OP_TEMPLATE_MACRO(Extensions, Extensions, &)
FASTGLTF_UNARY_OP_TEMPLATE_MACRO(Extensions, ~)

constexpr Extensions operator-(const Extensions& a, const std::underlying_type_t<Extensions>& b) noexcept {
FASTGLTF_EXPORT constexpr Extensions operator-(const Extensions& a, const std::underlying_type_t<Extensions>& b) noexcept {
static_assert(std::is_enum_v<Extensions>);
return static_cast<Extensions>(to_underlying(a) - b);
}

// clang-format off
enum class Options : std::uint64_t {
FASTGLTF_EXPORT enum class Options : std::uint64_t {
None = 0,
/**
* This allows 5130 as an accessor component type. 5130 is the OpenGL constant GL_DOUBLE,
Expand Down Expand Up @@ -277,7 +279,7 @@ namespace fastgltf {
GenerateMeshIndices = 1 << 8,
};

enum class ExportOptions : std::uint64_t {
FASTGLTF_EXPORT enum class ExportOptions : std::uint64_t {
None = 0,

/**
Expand Down Expand Up @@ -378,6 +380,7 @@ namespace fastgltf {
* @note If \p extensions has more than one bit set (multiple extensions), this
* will return the name of the first set bit.
*/
FASTGLTF_EXPORT
#if FASTGLTF_CPP_20
constexpr
#else
Expand All @@ -396,7 +399,7 @@ namespace fastgltf {
/**
* Returns a list of extension names based on the given extension flags.
*/
inline auto stringifyExtensionBits(Extensions extensions) -> decltype(Asset::extensionsRequired) {
FASTGLTF_EXPORT inline auto stringifyExtensionBits(Extensions extensions) -> decltype(Asset::extensionsRequired) {
decltype(Asset::extensionsRequired) stringified;
for (std::uint8_t i = 0; i < std::numeric_limits<std::underlying_type_t<Extensions>>::digits; ++i) {
// The 1 has to be cast to the underlying type as uint8_t(1) << 9 will overflow and be effectively the same as uint8_t(1).
Expand Down Expand Up @@ -568,24 +571,24 @@ namespace fastgltf {
}
};

struct BufferInfo {
FASTGLTF_EXPORT struct BufferInfo {
void* mappedMemory;
CustomBufferId customId;
};

using BufferMapCallback = BufferInfo(std::uint64_t bufferSize, void* userPointer);
using BufferUnmapCallback = void(BufferInfo* bufferInfo, void* userPointer);
using Base64DecodeCallback = void(std::string_view base64, std::uint8_t* dataOutput, std::size_t padding, std::size_t dataOutputSize, void* userPointer);
using ExtrasParseCallback = void(simdjson::dom::object* extras, std::size_t objectIndex, Category objectType, void* userPointer);
using ExtrasWriteCallback = std::optional<std::string>(std::size_t objectIndex, Category objectType, void* userPointer);
FASTGLTF_EXPORT using BufferMapCallback = BufferInfo(std::uint64_t bufferSize, void* userPointer);
FASTGLTF_EXPORT using BufferUnmapCallback = void(BufferInfo* bufferInfo, void* userPointer);
FASTGLTF_EXPORT using Base64DecodeCallback = void(std::string_view base64, std::uint8_t* dataOutput, std::size_t padding, std::size_t dataOutputSize, void* userPointer);
FASTGLTF_EXPORT using ExtrasParseCallback = void(simdjson::dom::object* extras, std::size_t objectIndex, Category objectType, void* userPointer);
FASTGLTF_EXPORT using ExtrasWriteCallback = std::optional<std::string>(std::size_t objectIndex, Category objectType, void* userPointer);

/**
* Enum to represent the type of a glTF file. glTFs can either be the standard JSON file with
* paths to buffers or with a base64 embedded buffers, or they can be in a so called GLB
* container format which has two or more chunks of binary data, where one represents buffers
* and the other contains the JSON string.
*/
enum class GltfType : std::uint8_t {
FASTGLTF_EXPORT enum class GltfType : std::uint8_t {
glTF,
GLB,
Invalid,
Expand All @@ -600,7 +603,7 @@ namespace fastgltf {
* @return The type of the glTF file, either glTF, GLB, or Invalid if it was not determinable. If this function
* returns Invalid it is highly likely that the buffer does not actually represent a valid glTF file.
*/
GltfType determineGltfFileType(GltfDataGetter& data);
FASTGLTF_EXPORT GltfType determineGltfFileType(GltfDataGetter& data);

/**
* This interface defines how the parser can read the bytes making up a glTF or GLB file.
Expand Down Expand Up @@ -633,7 +636,7 @@ namespace fastgltf {
[[nodiscard]] virtual std::size_t totalSize() = 0;
};

class GltfDataBuffer : public GltfDataGetter {
FASTGLTF_EXPORT class GltfDataBuffer : public GltfDataGetter {
protected:
std::unique_ptr<std::byte[]> buffer;

Expand Down Expand Up @@ -707,7 +710,7 @@ namespace fastgltf {
* Memory-maps a file. This uses mmap on macOS and Linux, and MapViewOfFile on Windows, and is not available elsewhere.
* You should check for FASTGLTF_HAS_MEMORY_MAPPED_FILE before using this class.
*/
class MappedGltfFile : public GltfDataGetter {
FASTGLTF_EXPORT class MappedGltfFile : public GltfDataGetter {
void* mappedFile;
#if defined(_WIN32)
// Windows requires us to keep the file handle alive. Win32 HANDLE is a void*.
Expand Down Expand Up @@ -755,7 +758,7 @@ namespace fastgltf {
};
#endif

class GltfFileStream : public GltfDataGetter {
FASTGLTF_EXPORT class GltfFileStream : public GltfDataGetter {
std::ifstream fileStream;
std::vector<std::ifstream::char_type> buf;

Expand All @@ -779,9 +782,9 @@ namespace fastgltf {
};

#if defined(__ANDROID__)
void setAndroidAssetManager(AAssetManager* assetManager) noexcept;
FASTGLTF_EXPORT void setAndroidAssetManager(AAssetManager* assetManager) noexcept;

class AndroidGltfDataBuffer : public GltfDataBuffer {
FASTGLTF_EXPORT class AndroidGltfDataBuffer : public GltfDataBuffer {
explicit AndroidGltfDataBuffer(const std::filesystem::path& path, std::uint64_t byteOffset) noexcept;

public:
Expand All @@ -807,7 +810,7 @@ namespace fastgltf {
* Realistically, this should not be necessary in Release applications, but could be helpful
* when debugging an asset related issue.
*/
[[nodiscard]] Error validate(const Asset& asset);
FASTGLTF_EXPORT [[nodiscard]] Error validate(const Asset& asset);

/**
* Some internals the parser passes on to each glTF instance.
Expand Down Expand Up @@ -950,7 +953,7 @@ namespace fastgltf {
*/
std::string escapeString(std::string_view string);

template <typename T>
FASTGLTF_EXPORT template <typename T>
struct ExportResult {
T output;

Expand All @@ -965,7 +968,7 @@ namespace fastgltf {
* into memory structures, which can then be used to manually write them to disk.
* If you want to let fastgltf handle the file writing too, use fastgltf::FileExporter.
*/
class Exporter {
FASTGLTF_EXPORT class Exporter {
protected:
Error errorCode = Error::None;
ExportOptions options = ExportOptions::None;
Expand Down Expand Up @@ -1037,7 +1040,7 @@ namespace fastgltf {
* This exporter builds upon Exporter by writing all files automatically to the
* given paths.
*/
class FileExporter : public Exporter {
FASTGLTF_EXPORT class FileExporter : public Exporter {
using Exporter::writeGltfJson;
using Exporter::writeGltfBinary;

Expand Down
Loading