Skip to content

Commit

Permalink
C++ Client: vendor the "fmt" and "date" projects and start to use them
Browse files Browse the repository at this point in the history
  • Loading branch information
kosak committed Nov 29, 2023
1 parent 0974068 commit 580e220
Show file tree
Hide file tree
Showing 27 changed files with 24,411 additions and 93 deletions.
32 changes: 29 additions & 3 deletions cpp-client/deephaven/dhcore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ set(ALL_FILES
src/ticking/shift_processor.cc
src/ticking/space_mapper.cc
src/ticking/ticking.cc
src/utility/cython_support.cc
src/utility/cython_support.cc
src/utility/utility.cc

include/private/deephaven/dhcore/ticking/immer_table_state.h
Expand All @@ -49,11 +49,12 @@ set(ALL_FILES
include/public/deephaven/dhcore/container/row_sequence.h
include/public/deephaven/dhcore/ticking/barrage_processor.h
include/public/deephaven/dhcore/ticking/ticking.h
include/public/deephaven/dhcore/utility/cython_support.h
include/public/deephaven/dhcore/utility/cython_support.h
include/public/deephaven/dhcore/utility/utility.h

flatbuf/deephaven/flatbuf/Barrage_generated.h

# we vendor flatbuffers but it is private to us
third_party/flatbuffers/include/flatbuffers/allocator.h
third_party/flatbuffers/include/flatbuffers/array.h
third_party/flatbuffers/include/flatbuffers/base.h
Expand All @@ -73,7 +74,29 @@ set(ALL_FILES
third_party/flatbuffers/include/flatbuffers/verifier.h

third_party/roaring/include/roaring/roaring.c
)

# The way we vendor the "fmt" library is we compile the cc files directly into dhcore,
# and we publish the .h files at deephaven/third_party/fmt
third_party/fmt/src/format.cc
third_party/fmt/src/os.cc

include/public/deephaven/third_party/fmt/args.h
include/public/deephaven/third_party/fmt/chrono.h
include/public/deephaven/third_party/fmt/color.h
include/public/deephaven/third_party/fmt/compile.h
include/public/deephaven/third_party/fmt/core.h
include/public/deephaven/third_party/fmt/format-inl.h
include/public/deephaven/third_party/fmt/format.h
include/public/deephaven/third_party/fmt/os.h
include/public/deephaven/third_party/fmt/ostream.h
include/public/deephaven/third_party/fmt/printf.h
include/public/deephaven/third_party/fmt/ranges.h
include/public/deephaven/third_party/fmt/std.h
include/public/deephaven/third_party/fmt/xchar.h

# we vendor the date library also but it's private to us, so we don't export its .h file
third_party/date/include/date/date.h
)

add_library(dhcore_objlib OBJECT ${ALL_FILES})
# In order to make a shared library suitable for Cython.
Expand All @@ -83,6 +106,7 @@ target_compile_options(dhcore_objlib PRIVATE -Wall -Werror -Wno-deprecated-decla

target_include_directories(dhcore_objlib PRIVATE include/private)
target_include_directories(dhcore_objlib PRIVATE third_party/flatbuffers/include)
target_include_directories(dhcore_objlib PRIVATE third_party/date/include)
target_include_directories(dhcore_objlib PRIVATE third_party/roaring/include)
target_include_directories(dhcore_objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/public>)

Expand All @@ -103,6 +127,7 @@ get_property(object_link_libs TARGET dhcore_objlib PROPERTY LINK_LIBRARIES)
add_library(dhcore SHARED $<TARGET_OBJECTS:dhcore_objlib>)
# TODO: How to avoid repetition here for target_include_directories?
target_include_directories(dhcore PRIVATE include/private)
target_include_directories(dhcore PRIVATE third_party/date/include)
target_include_directories(dhcore PRIVATE third_party/flatbuffers/include)
target_include_directories(dhcore PRIVATE third_party/roaring/include)
target_include_directories(dhcore PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/public>)
Expand All @@ -111,6 +136,7 @@ target_link_libraries(dhcore PUBLIC ${object_link_libs})
add_library(dhcore_static STATIC $<TARGET_OBJECTS:dhcore_objlib>)
# TODO: How to avoid repetition here for target_include_directories?
target_include_directories(dhcore_static PRIVATE include/private)
target_include_directories(dhcore_static PRIVATE third_party/date/include)
target_include_directories(dhcore_static PRIVATE third_party/flatbuffers/include)
target_include_directories(dhcore_static PRIVATE third_party/roaring/include)
target_include_directories(dhcore_static PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/public>)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <cstdint>
#include <ostream>
#include "deephaven/dhcore/utility/utility.h"
#include "deephaven/third_party/fmt/ostream.h"

namespace deephaven::dhcore {
struct ElementTypeId {
Expand Down Expand Up @@ -392,3 +393,5 @@ class DateTime {
friend std::ostream &operator<<(std::ostream &s, const DateTime &o);
};
} // namespace deephaven::dhcore

template<> struct fmt::formatter<deephaven::dhcore::DateTime> : ostream_formatter {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
// Formatting library for C++ - dynamic argument lists
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.

#ifndef FMT_ARGS_H_
#define FMT_ARGS_H_

#include <functional> // std::reference_wrapper
#include <memory> // std::unique_ptr
#include <vector>

#include "core.h"

FMT_BEGIN_NAMESPACE

namespace detail {

template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};

template <typename T> const T& unwrap(const T& v) { return v; }
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
return static_cast<const T&>(v);
}

class dynamic_arg_list {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};

template <typename T> struct typed_node : node<> {
T value;

template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}

template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}
};

std::unique_ptr<node<>> head_;

public:
template <typename T, typename Arg> const T& push(const Arg& arg) {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
head_ = std::move(new_node);
return value;
}
};
} // namespace detail

/**
\rst
A dynamic version of `fmt::format_arg_store`.
It's equipped with a storage to potentially temporary objects which lifetimes
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/
template <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
using char_type = typename Context::char_type;

template <typename T> struct need_copy {
static constexpr detail::type mapped_type =
detail::mapped_type_constant<T, Context>::value;

enum {
value = !(detail::is_reference_wrapper<T>::value ||
std::is_same<T, basic_string_view<char_type>>::value ||
std::is_same<T, detail::std_string_view<char_type>>::value ||
(mapped_type != detail::type::cstring_type &&
mapped_type != detail::type::string_type &&
mapped_type != detail::type::custom_type))
};
};

template <typename T>
using stored_type = conditional_t<
std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>;

// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_;
std::vector<detail::named_arg_info<char_type>> named_info_;

// Storage of arguments not fitting into basic_format_arg must grow
// without relocation because items in data_ refer to it.
detail::dynamic_arg_list dynamic_args_;

friend class basic_format_args<Context>;

unsigned long long get_types() const {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}

const basic_format_arg<Context>* data() const {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}

template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(detail::make_arg<Context>(arg));
}

template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
if (named_info_.empty()) {
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
data_.insert(data_.begin(), {zero_ptr, 0});
}
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
data->pop_back();
};
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
guard{&data_, pop_one};
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
guard.release();
}

public:
constexpr dynamic_format_arg_store() = default;

/**
\rst
Adds an argument into the dynamic store for later passing to a formatting
function.
Note that custom types and string types (but not string views) are copied
into the store dynamically allocating memory if necessary.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
}

/**
\rst
Adds a reference to the argument into the dynamic store for later passing to
a formatting function.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
char band[] = "Rolling Stones";
store.push_back(std::cref(band));
band[9] = 'c'; // Changing str affects the output.
std::string result = fmt::vformat("{}", store);
// result == "Rolling Scones"
\endrst
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}

/**
Adds named argument into the dynamic store for later passing to a formatting
function. ``std::reference_wrapper`` is supported to avoid copying of the
argument. The name is always copied into the store.
*/
template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) {
emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
} else {
emplace_arg(fmt::arg(arg_name, arg.value));
}
}

/** Erase all elements from the store */
void clear() {
data_.clear();
named_info_.clear();
dynamic_args_ = detail::dynamic_arg_list();
}

/**
\rst
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named,
"Set of arguments includes set of named arguments");
data_.reserve(new_cap);
named_info_.reserve(new_cap_named);
}
};

FMT_END_NAMESPACE

#endif // FMT_ARGS_H_
Loading

0 comments on commit 580e220

Please sign in to comment.