| @@ -3,4 +3,6 @@ __pycache__/ | |||
| .peru/ | |||
| .vscode/ | |||
| .mypy_cache/ | |||
| *.dsd/ | |||
| *.dsd/ | |||
| _prebuilt/ | |||
| .dds-repo-lock | |||
| @@ -9,16 +9,11 @@ jobs: | |||
| - script: | | |||
| echo Loading VS environment | |||
| call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\Common7\\Tools\\vsdevcmd" -arch=x64 || exit 1 | |||
| echo Executing build/test script | |||
| python -u tools/build.py --cxx cl.exe --test --static || exit 1 | |||
| displayName: Build and Run Unit Tests | |||
| echo Executing Build and Tests | |||
| python -u tools/ci.py --cxx cl.exe -T tools\\msvc.dds || exit 1 | |||
| displayName: Full CI | |||
| - publish: _build/dds.exe | |||
| artifact: DDS Executable - Windows VS2019 | |||
| - script: | | |||
| echo Loading VS environment | |||
| call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\Common7\\Tools\\vsdevcmd" -arch=x64 || exit 1 | |||
| python -u tools/test.py --exe _build/dds.exe -T tools/msvc.dds || exit 1 | |||
| displayName: Smoke Tests | |||
| - job: Linux_GCC8 | |||
| pool: | |||
| @@ -26,12 +21,10 @@ jobs: | |||
| steps: | |||
| - script: sudo apt update -y && sudo apt install -y python3-minimal g++-8 | |||
| displayName: Prepare System | |||
| - script: python3 -u tools/build.py --cxx g++-8 --test --static | |||
| displayName: Build and Run Unit Tests | |||
| - script: python3 -u tools/ci.py --cxx g++-8 -T tools/gcc-8.dds | |||
| displayName: Full CI | |||
| - publish: _build/dds | |||
| artifact: DDS Executable - Linux | |||
| - script: python3 -u tools/test.py --exe _build/dds -T:gcc-8 | |||
| displayName: Smoke Tests | |||
| - job: macOS_GCC8 | |||
| pool: | |||
| @@ -39,9 +32,7 @@ jobs: | |||
| steps: | |||
| - script: brew install gcc@8 | |||
| displayName: Prepare System | |||
| - script: python3 -u tools/build.py --cxx g++-8 --test | |||
| - script: python3 -u tools/ci.py --cxx g++-8 -T tools/gcc-8.dds | |||
| displayName: Build and Run Unit Tests | |||
| - publish: _build/dds | |||
| artifact: DDS Executable - macOS | |||
| - script: python3 -u tools/test.py --exe _build/dds -T:gcc-8 | |||
| displayName: Smoke Tests | |||
| @@ -2,4 +2,4 @@ Type: Library | |||
| Name: args | |||
| Include-Path: taywee-args/include | |||
| Include-Path: repo/taywee-args/include | |||
| @@ -1,6 +1,6 @@ | |||
| Type: Package | |||
| Name: ms-third | |||
| Namespace: ms | |||
| Namespace: Microsoft | |||
| Library: wil.lml | |||
| @@ -1,4 +1,4 @@ | |||
| Type: Library | |||
| Name: json | |||
| Include-Path: nlohmann-json/include | |||
| Include-Path: repo/nlohmann-json/include | |||
| @@ -1,4 +1,4 @@ | |||
| Type: Library | |||
| Name: ranges-v3 | |||
| Include-Path: ranges-v3/include | |||
| Name: range-v3 | |||
| Include-Path: repo/range-v3/include | |||
| @@ -0,0 +1,2 @@ | |||
| _build/ | |||
| .vscode/ | |||
| @@ -0,0 +1,12 @@ | |||
| ; DO NOT EDIT (unless you know what you are doing) | |||
| ; | |||
| ; This subdirectory is a git "subrepo", and this file is maintained by the | |||
| ; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme | |||
| ; | |||
| [subrepo] | |||
| remote = git@github.com:vector-of-bool/neo-buffer.git | |||
| branch = develop | |||
| commit = 9461eded7f661b31bcdf4f8760ebe4ebc83e9630 | |||
| parent = 4bc28954ef34869a5b5ad65094c6f56a23937f8b | |||
| method = merge | |||
| cmdver = 0.4.0 | |||
| @@ -0,0 +1 @@ | |||
| Name: buffer | |||
| @@ -0,0 +1,3 @@ | |||
| Name: neo-buffer | |||
| Namespace: neo | |||
| Version: 0.1.0 | |||
| @@ -0,0 +1,61 @@ | |||
| #pragma once | |||
| #include <cstddef> | |||
| #include <iterator> | |||
| namespace neo { | |||
| namespace detail { | |||
| class single_buffer_iter_sentinel {}; | |||
| template <typename Buffer> | |||
| class single_buffer_iter { | |||
| Buffer _buf; | |||
| bool _dead = false; | |||
| public: | |||
| using difference_type = std::ptrdiff_t; | |||
| using value_type = Buffer; | |||
| using pointer = const value_type*; | |||
| using reference = const value_type&; | |||
| using iterator_category = std::forward_iterator_tag; | |||
| constexpr single_buffer_iter(Buffer b) noexcept | |||
| : _buf(b) {} | |||
| constexpr reference operator*() const noexcept { return _buf; } | |||
| constexpr pointer operator->() const noexcept { return &_buf; } | |||
| constexpr bool operator==(single_buffer_iter_sentinel) const noexcept { return _dead; } | |||
| constexpr bool operator!=(single_buffer_iter_sentinel) const noexcept { return !_dead; } | |||
| constexpr bool operator==(single_buffer_iter o) const noexcept { return _dead == o._dead; } | |||
| constexpr bool operator!=(single_buffer_iter o) const noexcept { return !(*this == o); } | |||
| constexpr single_buffer_iter operator++(int) noexcept { | |||
| auto me = *this; | |||
| _dead = true; | |||
| return me; | |||
| } | |||
| constexpr single_buffer_iter& operator++() noexcept { | |||
| _dead = true; | |||
| return *this; | |||
| } | |||
| }; | |||
| } // namespace detail | |||
| template <typename T> | |||
| constexpr std::byte* byte_pointer(T* ptr) noexcept requires std::is_trivial_v<T> { | |||
| auto void_ = static_cast<void*>(ptr); | |||
| return static_cast<std::byte*>(void_); | |||
| } | |||
| template <typename T> | |||
| constexpr const std::byte* byte_pointer(const T* ptr) noexcept requires std::is_trivial_v<T> { | |||
| auto void_ = static_cast<const void*>(ptr); | |||
| return static_cast<const std::byte*>(void_); | |||
| } | |||
| } // namespace neo | |||
| @@ -0,0 +1 @@ | |||
| #include "./buffer.hpp" | |||
| @@ -0,0 +1,109 @@ | |||
| #pragma once | |||
| #include <neo/const_buffer.hpp> | |||
| #include <neo/mutable_buffer.hpp> | |||
| #include <string> | |||
| #include <string_view> | |||
| #include <type_traits> | |||
| namespace neo { | |||
| constexpr mutable_buffer buffer(const mutable_buffer& b) noexcept { return b; } | |||
| constexpr mutable_buffer buffer(const mutable_buffer& b, mutable_buffer::size_type s) noexcept { | |||
| auto min_size = s > b.size() ? b.size() : s; | |||
| return mutable_buffer(b.data(), min_size); | |||
| } | |||
| constexpr const_buffer buffer(const const_buffer& b) noexcept { return b; } | |||
| constexpr const_buffer buffer(const const_buffer& b, const_buffer::size_type s) noexcept { | |||
| auto min_size = s > b.size() ? b.size() : s; | |||
| return const_buffer(b.data(), min_size); | |||
| } | |||
| // ############################################################################# | |||
| /** | |||
| * Create a mutable buffer from an opaque pointer to void | |||
| */ | |||
| constexpr mutable_buffer buffer(void* ptr, mutable_buffer::size_type s) noexcept { | |||
| return mutable_buffer(static_cast<std::byte*>(ptr), s); | |||
| } | |||
| /** | |||
| * Create an immutable buffer from an opaque pointer to void | |||
| */ | |||
| constexpr const_buffer buffer(const void* ptr, const_buffer::size_type s) noexcept { | |||
| return const_buffer(static_cast<const std::byte*>(ptr), s); | |||
| } | |||
| // ############################################################################# | |||
| /** | |||
| * Create a mutable buffer that refers to the bytes of a trivial object. | |||
| */ | |||
| template <typename Trivial, | |||
| typename | |||
| = std::enable_if_t<!std::is_const_v<Trivial> && !std::is_same_v<Trivial, mutable_buffer>>> | |||
| constexpr mutable_buffer buffer(Trivial& item, std::size_t max_size = sizeof(Trivial)) { | |||
| auto min_size = max_size > sizeof(item) ? sizeof(item) : max_size; | |||
| return mutable_buffer(byte_pointer(std::addressof(item)), min_size); | |||
| } | |||
| /** | |||
| * Create an immutable buffer that refers to the bytes of a trivial object. | |||
| */ | |||
| template < | |||
| typename Trivial, | |||
| typename = std::enable_if_t< | |||
| std::is_const_v< | |||
| Trivial> && !std::is_same_v<Trivial, mutable_buffer> && !std::is_same_v<Trivial, const_buffer>>> | |||
| constexpr const_buffer buffer(Trivial& item, std::size_t max_size = sizeof(Trivial)) { | |||
| auto min_size = std::min(sizeof(item), max_size); | |||
| return const_buffer(byte_pointer(std::addressof(item)), min_size); | |||
| } | |||
| // ############################################################################# | |||
| /** | |||
| * Create a mutable buffer referring to the characters of a basic_string object | |||
| */ | |||
| template <typename Char, typename Traits, typename Alloc> | |||
| constexpr mutable_buffer buffer(std::basic_string<Char, Traits, Alloc>& str, std::size_t max_size) { | |||
| auto use_size = max_size > str.size() ? str.size() : max_size; | |||
| return buffer(str.data(), use_size); | |||
| } | |||
| template <typename Char, typename Traits, typename Alloc> | |||
| constexpr mutable_buffer buffer(std::basic_string<Char, Traits, Alloc>& str) { | |||
| return buffer(str, str.size()); | |||
| } | |||
| /** | |||
| * Create an immutable buffer refering to the characters of a basic_string object | |||
| */ | |||
| template <typename Char, typename Traits, typename Alloc> | |||
| constexpr const_buffer buffer(const std::basic_string<Char, Traits, Alloc>& str, | |||
| std::size_t max_size) { | |||
| auto use_size = max_size > str.size() ? str.size() : max_size; | |||
| return buffer(str.data(), use_size); | |||
| } | |||
| template <typename Char, typename Traits, typename Alloc> | |||
| constexpr const_buffer buffer(const std::basic_string<Char, Traits, Alloc>& str) { | |||
| return buffer(str, str.size()); | |||
| } | |||
| // ############################################################################# | |||
| /** | |||
| * Create an immutable buffer referring to the characters of a basic_string_view | |||
| */ | |||
| template <typename Char, typename Traits> | |||
| constexpr const_buffer buffer(std::basic_string_view<Char, Traits> sv, std::size_t max_size) { | |||
| auto use_size = max_size > sv.size() ? sv.size() : max_size; | |||
| return buffer(sv.data(), use_size); | |||
| } | |||
| template <typename Char, typename Traits> | |||
| constexpr const_buffer buffer(std::basic_string_view<Char, Traits> sv) { | |||
| return buffer(sv, sv.size()); | |||
| } | |||
| } // namespace neo | |||
| @@ -0,0 +1,38 @@ | |||
| #include <neo/buffer.hpp> | |||
| #include <neo/buffer.test.hpp> | |||
| #include <neo/buffer_algorithm.hpp> | |||
| #include <array> | |||
| #include <string> | |||
| struct my_simple_struct { | |||
| int a; | |||
| int b; | |||
| }; | |||
| int main() { | |||
| // std::string s = "I am a string!"; | |||
| // auto s_buf = neo::buffer(s); | |||
| // CHECK(s_buf.data() == s.data()); | |||
| my_simple_struct foo; | |||
| foo.a = 12; | |||
| foo.b = 3; | |||
| neo::mutable_buffer pod_buf = neo::buffer(foo); | |||
| CHECK(pod_buf.size() == sizeof(foo)); | |||
| my_simple_struct bar; | |||
| neo::buffer_copy(neo::buffer(bar), pod_buf); | |||
| CHECK(bar.a == foo.a); | |||
| CHECK(bar.b == foo.b); | |||
| bar.b = 55; | |||
| std::array<char, sizeof bar> buf; | |||
| neo::buffer_copy(neo::buffer(buf), neo::buffer(bar)); | |||
| neo::buffer_copy(neo::buffer(foo), neo::buffer(buf)); | |||
| CHECK(foo.b == 55); | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| #pragma once | |||
| #include <iostream> | |||
| #define CHECK(...) \ | |||
| do { \ | |||
| if (!(__VA_ARGS__)) { \ | |||
| std::cerr << "Check failed: " << (#__VA_ARGS__) << '\n'; \ | |||
| return 1; \ | |||
| } \ | |||
| } while (0) | |||
| @@ -0,0 +1,71 @@ | |||
| #pragma once | |||
| #include <neo/buffer_concepts.hpp> | |||
| #include <neo/const_buffer.hpp> | |||
| #include <neo/mutable_buffer.hpp> | |||
| #include <algorithm> | |||
| #include <cstddef> | |||
| #include <cstring> | |||
| namespace neo { | |||
| template <typename Seq> | |||
| constexpr std::size_t buffer_size(const Seq& seq) { | |||
| auto iter = buffer_sequence_begin(seq); | |||
| const auto stop = buffer_sequence_end(seq); | |||
| std::size_t size = 0; | |||
| while (iter != stop) { | |||
| size += static_cast<std::size_t>(iter->size()); | |||
| ++iter; | |||
| } | |||
| return size; | |||
| } | |||
| template <mutable_buffer_sequence MutableSeq, const_buffer_sequence ConstSeq> | |||
| constexpr std::size_t | |||
| buffer_copy(const MutableSeq& dest, const ConstSeq& src, std::size_t max_copy) { | |||
| auto remaining_to_copy = max_copy; | |||
| std::size_t n_copied = 0; | |||
| auto dest_iter = buffer_sequence_begin(dest); | |||
| const auto dest_stop = buffer_sequence_end(dest); | |||
| auto src_iter = buffer_sequence_begin(src); | |||
| const auto src_stop = buffer_sequence_end(dest); | |||
| std::size_t src_offset = 0; | |||
| std::size_t dest_offset = 0; | |||
| while (dest_iter != dest_stop && src_iter != src_stop && remaining_to_copy) { | |||
| const_buffer src_buf = *src_iter + src_offset; | |||
| mutable_buffer dest_buf = *dest_iter + dest_offset; | |||
| const auto copy_now | |||
| = std::min(src_buf.size(), std::min(dest_buf.size(), remaining_to_copy)); | |||
| std::memcpy(dest_buf.data(), src_buf.data(), copy_now); | |||
| n_copied += copy_now; | |||
| src_buf += n_copied; | |||
| dest_buf += n_copied; | |||
| src_offset += n_copied; | |||
| dest_offset += n_copied; | |||
| if (src_buf.size() == 0) { | |||
| ++src_iter; | |||
| src_offset = 0; | |||
| } | |||
| if (dest_buf.size() == 0) { | |||
| ++dest_iter; | |||
| dest_offset = 0; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| template <typename MutableSeq, typename ConstSeq> | |||
| constexpr std::size_t buffer_copy(const MutableSeq& dest, const ConstSeq& src) { | |||
| auto src_size = buffer_size(src); | |||
| auto dest_size = buffer_size(dest); | |||
| auto min_size = (src_size > dest_size) ? dest_size : src_size; | |||
| return buffer_copy(dest, src, min_size); | |||
| } | |||
| } // namespace neo | |||
| @@ -0,0 +1,18 @@ | |||
| #include <neo/buffer_algorithm.hpp> | |||
| #include <neo/buffer.test.hpp> | |||
| #include <neo/const_buffer.hpp> | |||
| #include <string_view> | |||
| int main() { | |||
| auto buf = neo::const_buffer("A string"); | |||
| auto buf_iter = neo::buffer_sequence_begin(buf); | |||
| CHECK(buf_iter->data() == buf.data()); | |||
| CHECK(buf_iter != neo::buffer_sequence_end(buf)); | |||
| CHECK(neo::buffer_size(buf) == buf.size()); | |||
| // neo::buffer_size(12); | |||
| } | |||
| @@ -0,0 +1,46 @@ | |||
| #pragma once | |||
| #ifndef NEO_CONCEPT | |||
| #if defined(__GNUC__) && __GNUC__ < 9 | |||
| #define NEO_CONCEPT concept bool | |||
| #else | |||
| #define NEO_CONCEPT concept | |||
| #endif | |||
| #endif | |||
| #include <neo/buffer_seq_iter.hpp> | |||
| #include <neo/const_buffer.hpp> | |||
| #include <neo/mutable_buffer.hpp> | |||
| #include <iterator> | |||
| namespace neo { | |||
| template <typename T> | |||
| NEO_CONCEPT const_buffer_sequence_iterator = requires(T iter) { | |||
| typename std::iterator_traits<T>::value_type; | |||
| std::is_convertible_v<typename std::iterator_traits<T>::value_type, const_buffer>; | |||
| }; | |||
| template <typename T> | |||
| NEO_CONCEPT mutable_buffer_sequence_iterator | |||
| = const_buffer_sequence_iterator<T>&& requires(T iter) { | |||
| std::is_convertible_v<typename std::iterator_traits<T>::value_type, mutable_buffer>; | |||
| }; | |||
| template <typename T> | |||
| NEO_CONCEPT const_buffer_sequence = requires(T seq) { | |||
| { buffer_sequence_begin(seq) } | |||
| ->const_buffer_sequence_iterator; | |||
| buffer_sequence_end(seq); | |||
| { buffer_sequence_begin(seq) != buffer_sequence_end(seq) } | |||
| ->bool; | |||
| }; | |||
| template <typename T> | |||
| NEO_CONCEPT mutable_buffer_sequence = const_buffer_sequence<T>&& requires(T seq) { | |||
| { buffer_sequence_begin(seq) } | |||
| ->mutable_buffer_sequence_iterator; | |||
| }; | |||
| } // namespace neo | |||
| @@ -0,0 +1,47 @@ | |||
| #pragma once | |||
| #include <utility> | |||
| namespace neo { | |||
| constexpr inline struct buffer_sequence_begin_fn { | |||
| template <typename T> | |||
| decltype(auto) operator()(T&& t) const requires requires { | |||
| buffer_sequence_begin(t); | |||
| } | |||
| { return buffer_sequence_begin(t); } | |||
| template <typename T> | |||
| decltype(auto) operator()(T&& t) const requires requires { | |||
| t.buffer_sequence_begin(); | |||
| } | |||
| { return t.buffer_sequence_begin(); } | |||
| template <typename T> | |||
| decltype(auto) operator()(T&& t) const requires requires { | |||
| _impl_buffer_sequence_begin(t); | |||
| } | |||
| { return _impl_buffer_sequence_begin(t); } | |||
| } buffer_sequence_begin; | |||
| inline struct buffer_sequence_end_fn { | |||
| template <typename T> | |||
| decltype(auto) operator()(T&& t) const requires requires { | |||
| buffer_sequence_end(t); | |||
| } | |||
| { return buffer_sequence_end(t); } | |||
| template <typename T> | |||
| decltype(auto) operator()(T&& t) const requires requires { | |||
| t.buffer_sequence_end(); | |||
| } | |||
| { return t.buffer_sequence_end(); } | |||
| template <typename T> | |||
| decltype(auto) operator()(T&& t) const requires requires { | |||
| _impl_buffer_sequence_end(t); | |||
| } | |||
| { return _impl_buffer_sequence_end(t); } | |||
| } buffer_sequence_end; | |||
| } // namespace neo | |||
| @@ -0,0 +1,64 @@ | |||
| #pragma once | |||
| #include <neo/base_buffer.hpp> | |||
| #include <neo/mutable_buffer.hpp> | |||
| #include <cassert> | |||
| #include <cstddef> | |||
| #include <string_view> | |||
| namespace neo { | |||
| /** | |||
| * A type that represents a view to a readonly segment of contiguous memory. | |||
| */ | |||
| class const_buffer { | |||
| public: | |||
| using pointer = const std::byte*; | |||
| using size_type = std::size_t; | |||
| private: | |||
| pointer _data = nullptr; | |||
| size_type _size = 0; | |||
| public: | |||
| constexpr const_buffer() noexcept = default; | |||
| constexpr const_buffer(pointer ptr, size_type size) noexcept | |||
| : _data(ptr) | |||
| , _size(size) {} | |||
| constexpr const_buffer(mutable_buffer buf) | |||
| : _data(buf.data()) | |||
| , _size(buf.size()) {} | |||
| explicit constexpr const_buffer(std::string_view sv) | |||
| : _data(byte_pointer(sv.data())) | |||
| , _size(sv.size()) {} | |||
| constexpr pointer data() const noexcept { return _data; } | |||
| constexpr pointer data_end() const noexcept { return _data + size(); } | |||
| constexpr size_type size() const noexcept { return _size; } | |||
| constexpr const_buffer& operator+=(size_type s) noexcept { | |||
| assert(s <= size() && "Advanced neo::const_buffer past-the-end"); | |||
| _data += s; | |||
| _size -= s; | |||
| return *this; | |||
| } | |||
| }; | |||
| inline constexpr const_buffer operator+(const_buffer buf, const_buffer::size_type s) noexcept { | |||
| auto copy = buf; | |||
| copy += s; | |||
| return copy; | |||
| } | |||
| inline constexpr auto _impl_buffer_sequence_begin(const_buffer buf) noexcept { | |||
| return detail::single_buffer_iter(buf); | |||
| } | |||
| inline constexpr auto _impl_buffer_sequence_end(const_buffer) noexcept { | |||
| return detail::single_buffer_iter_sentinel(); | |||
| } | |||
| } // namespace neo | |||
| @@ -0,0 +1,17 @@ | |||
| #include <neo/buffer.test.hpp> | |||
| #include <neo/const_buffer.hpp> | |||
| #include <iostream> | |||
| int main() { | |||
| neo::const_buffer buf; | |||
| CHECK(buf.size() == 0); | |||
| buf = neo::const_buffer("meow"); | |||
| CHECK(buf.size() == 4); | |||
| auto buf2 = buf + 3; | |||
| CHECK(buf2.size() == 1); | |||
| CHECK(buf2.data()[0] == std::byte('w')); | |||
| buf2 += 1; | |||
| CHECK(buf2.size() == 0); | |||
| } | |||
| @@ -0,0 +1,52 @@ | |||
| #pragma once | |||
| #include <neo/base_buffer.hpp> | |||
| #include <cstddef> | |||
| #include <iterator> | |||
| #include <type_traits> | |||
| namespace neo { | |||
| class mutable_buffer { | |||
| public: | |||
| using pointer = std::byte*; | |||
| using size_type = std::size_t; | |||
| private: | |||
| pointer _data = nullptr; | |||
| size_type _size = 0; | |||
| public: | |||
| constexpr mutable_buffer() noexcept = default; | |||
| constexpr mutable_buffer(pointer p, size_type size) noexcept | |||
| : _data(p) | |||
| , _size(size) {} | |||
| constexpr mutable_buffer& operator+=(size_type s) noexcept { | |||
| _data += s; | |||
| _size -= s; | |||
| return *this; | |||
| } | |||
| constexpr pointer data() const noexcept { return _data; } | |||
| constexpr pointer data_end() const noexcept { return _data + size(); } | |||
| constexpr size_type size() const noexcept { return _size; } | |||
| }; | |||
| inline constexpr mutable_buffer operator+(mutable_buffer buf, | |||
| mutable_buffer::size_type s) noexcept { | |||
| auto copy = buf; | |||
| copy += s; | |||
| return copy; | |||
| } | |||
| inline constexpr auto _impl_buffer_sequence_begin(mutable_buffer buf) noexcept { | |||
| return detail::single_buffer_iter(buf); | |||
| } | |||
| inline constexpr auto _impl_buffer_sequence_end(mutable_buffer) noexcept { | |||
| return detail::single_buffer_iter_sentinel(); | |||
| } | |||
| } // namespace neo | |||
| @@ -0,0 +1,3 @@ | |||
| #include <neo/mutable_buffer.hpp> | |||
| int main() {} | |||
| @@ -0,0 +1,4 @@ | |||
| Compiler-ID: GNU | |||
| C++-Compiler: g++-8 | |||
| C++-Version: C++17 | |||
| Flags: -fconcepts | |||
| @@ -0,0 +1 @@ | |||
| Name: json | |||
| @@ -0,0 +1,3 @@ | |||
| Name: nlohmann-json | |||
| Namespace: nlohmann | |||
| Version: 3.7.0 | |||