Skip to content

Commit

Permalink
* Added struct mbo::types::ContainerProxy which allows to add conta…
Browse files Browse the repository at this point in the history
…iner access to other types including smart pointers of containers.

* Added struct `mbo::types::OpaquePtr` an opaque alternative to `std::unique_ptr` which works with forward declared types.
* Added struct `mbo::types::OpaqueValue` an `OpaquePtr` with direct access, comparison and hashing which will not allow a nullptr.
* Added struct `mbo::types::OpaqueContainer` an `OpaqueValue` with direct container access.
* Changed pre-commit to use clang-format 19.1.6.
  • Loading branch information
helly25 committed Feb 22, 2025
1 parent 013ae78 commit 7bb5d0d
Show file tree
Hide file tree
Showing 8 changed files with 584 additions and 4 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
rev: v3.0.0
hooks:
- id: check-merge-conflict
- id: check-yaml
Expand All @@ -10,6 +10,6 @@ repos:
exclude: ^mbo/diff/tests/abc_whitespace.*$

- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v16.0.4
rev: v19.1.6
hooks:
- id: clang-format
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
* Fixed various issues for bazelmod based builds.
* Improved `mbo::testing::RunfilesDir/OrDie` to support a single param variant that understands bazel labels. Further add support for other repos than the current one by reading the repo mapping.
* Fixed `//mbo/file/ini:ini_file_test` to be able to pass when run as remote repository.
* Added struct `mbo::types::ContainerProxy` which allows to add container access to other types including smart pointers of containers.
* Added struct `mbo::types::OpaquePtr` an opaque alternative to `std::unique_ptr` which works with forward declared types.
* Added struct `mbo::types::OpaqueValue` an `OpaquePtr` with direct access, comparison and hashing which will not allow a nullptr.
* Added struct `mbo::types::OpaqueContainer` an `OpaqueValue` with direct container access.
* Changed pre-commit to use clang-format 19.1.6.

# 0.3.0

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ The C++ library is organized in functional groups each residing in their own dir
* meta-type `IfTrueThenVoid`: Helper type to inject default cases and to ensure the required type expansion is always possible.
* mbo/types:compare_cc, mbo/types/compare.h
* comparator `mbo::types::CompareLess` which is compatible to std::Less but allows container optimizations.
* mbo/types:container_proxy_cc, mbo/types/container_proxy.h
* struct `mbo::types::ContainerProxy` which allows to add container access to other types including smart pointers of containers.
* mbo/types:extend_cc, mbo/types/extend.h
* crtp-struct `Extend`: Enables extending of struct/class types with basic functionality.
* crtp-struct `ExtendNoDefault` Like `Extend` but without default extender functionality.
Expand All @@ -157,6 +159,10 @@ The C++ library is organized in functional groups each residing in their own dir
* mbo/types:no_destruct_cc, mbo/types/no_destruct.h
* struct `NoDestruct<T>`: Implements a type that allows to use any type as a static constant.
* Mainly, this prevents calling the destructor and thus prevents termination issues (initialization order fiasco).
* mbo/types:opaque_cc, mbo/types/opaque.h
* struct `mbo::types::OpaquePtr` an opaque alternative to `std::unique_ptr` which works with forward declared types.
* struct `mbo::types::OpaqueValue` an `OpaquePtr` with direct access, comparison and hashing which will not allow a nullptr.
* struct `mbo::types::OpaqueContainer` an `OpaqueValue` with direct container access.
* mbo/types:ref_wrap_cc, mbo/types/ref_wrap.h
* template-type `RefWrap<T>`: similar to `std::reference_wrapper` but supports operators `->` and `*`.
* mbo/types:required_cc, mbo/types/required.h
Expand Down
12 changes: 10 additions & 2 deletions mbo/mope/mope.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,21 @@ def _clang_format_impl(ctx, src, dst):
tools = clang_format_tool,
command = """
CLANG_FORMAT="{clang_format}"
if [ "{clang_format}" == "clang-format-auto" ] || [ -x "${{CLANG_FORMAT}}" ]; then
if [ "{clang_format}" == "clang-format-auto" ] || [ ! -x "${{CLANG_FORMAT}}" ]; then
if [ -x "external/llvm_toolchain_llvm/bin/clang-format" ]; then
CLANG_FORMAT="external/llvm_toolchain_llvm/bin/clang-format"
elif [ -x "${{LLVM_PATH}}/bin/clang-format" ]; then
CLANG_FORMAT="${{LLVM_PATH}}/bin/clang-format"
elif [ $(which "clang_format") ]; then
CLANG_FORMAT="clang_format"
elif [ $(which "clang-format-23") ]; then
CLANG_FORMAT="clang-format-23"
elif [ $(which "clang-format-22") ]; then
CLANG_FORMAT="clang-format-22"
elif [ $(which "clang-format-21") ]; then
CLANG_FORMAT="clang-format-21"
elif [ $(which "clang-format-20") ]; then
CLANG_FORMAT="clang-format-20"
elif [ $(which "clang-format-19") ]; then
CLANG_FORMAT="clang-format-19"
elif [ $(which "clang-format-18") ]; then
Expand Down Expand Up @@ -121,7 +129,7 @@ _clang_format_common_attrs = {
),
"_clang_format_tool": attr.string(
doc = "The target of the clang-format executable.",
default = CLANG_FORMAT_BINARY if type(CLANG_FORMAT_BINARY) == "string" else "clang-format",
default = CLANG_FORMAT_BINARY if type(CLANG_FORMAT_BINARY) == "string" else "clang-format-auto",
) if CLANG_FORMAT_BINARY else attr.label(
doc = "The target of the clang-format executable.",
default = Label("@llvm_toolchain_llvm//:bin/clang-format"),
Expand Down
24 changes: 24 additions & 0 deletions mbo/types/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,30 @@ cc_test(
],
)

cc_library(
name = "container_proxy_cc",
hdrs = ["container_proxy.h"],
)

cc_library(
name = "opaque_cc",
hdrs = ["opaque.h"],
visibility = ["//visibility:public"],
deps = [
":container_proxy_cc",
"@com_google_absl//absl/hash",
],
)

cc_test(
name = "opaque_test",
srcs = ["opaque_test.cc"],
deps = [
":opaque_cc",
"@com_google_googletest//:gtest_main",
],
)

cc_library(
name = "ref_wrap_cc",
hdrs = ["ref_wrap.h"],
Expand Down
183 changes: 183 additions & 0 deletions mbo/types/container_proxy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// SPDX-FileCopyrightText: Copyright (c) The helly25/mbo authors (helly25.com)
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef MBO_TYPES_CONTAINER_PROXY_H_
#define MBO_TYPES_CONTAINER_PROXY_H_

#include <compare>
#include <cstddef>
#include <type_traits>
#include <utility>

#include "absl/hash/hash.h"

namespace mbo::types {

// ContainerProxy is a wrapper for a type `T` which provides mutable and const access to a container.
//
// Example:
//
// ```
// // Data defines a struct with a field `data` that holds a container in a `std::unique_ptr`.
// // The type provides the two access methods `GetData`.
// template<typename Container = std::vector<std::string>>
// struct Data {
// using ContainerType = Container;
//
// Data() : data(std::make_unique<ContainerType>()) {}
//
// std::unique_ptr<ContainerType> data;
//
// ContainerType& GetData() { return *data; }
//
// const ContainerType& GetData() const { return *data; }
// };
//
// template<typename C = std::vector<std::string>>
// using Proxy = ContainerProxy<Data<C>, C, &C::GetData, &C::GetData>;
// ```
//
// In other words `ContainerProxy` allows to add container access to a field within a struct.
// This includes smart pointers like `std::unique_ptr` and `std::shared_ptr` which work directly.
// That means that the `ContainerProxy` can also be used to add container access to smart pointers.
//
// For further application check `mbo::types::OpaqueContainer` which allows direct member field
// wrapping without the need to change any other code.
// ```
template<
typename T,
typename Container = typename T::element_type,
Container& (T::*GetMutable)() = &T::operator*,
const Container& (T::*GetConst)() const = &T::operator*>
requires requires { typename std::remove_cvref_t<Container>::value_type; }
struct ContainerProxy : T {
private:
using C = std::remove_cvref_t<Container>;
using CC = const C;

constexpr auto& Get() { return (this->*GetMutable)(); }

constexpr const auto& Get() const { return (this->*GetConst)(); }

public:
using size_type = typename C::size_type;
using value_type = typename C::value_type;

// NOLINTBEGIN(readability-identifier-naming)
// clang-format off
constexpr auto begin() const requires(requires(const C& v) { v.begin(); }) { return Get().begin(); }
constexpr auto end() const requires(requires(const C& v) { v.end(); }) { return Get().end(); }
constexpr auto begin() requires(requires(C& v) { v.begin(); }) { return Get().begin(); }
constexpr auto end() requires(requires(C& v) { v.end(); }) { return Get().end(); }

constexpr auto rbegin() const requires(requires(const C& v) { v.rbegin(); }) { return Get().rbegin(); }
constexpr auto rend() const requires(requires(const C& v) { v.rend(); }) { return Get().rend(); }
constexpr auto rbegin() requires(requires(C& v) { v.rbegin(); }) { return Get().rbegin(); }
constexpr auto rend() requires(requires(C& v) { v.rend(); }) { return Get().rend(); }

constexpr auto cbegin() const requires(requires(const C& v) { v.cbegin(); }) { return Get().cbegin(); }
constexpr auto cend() const requires(requires(const C& v) { v.cend(); }) { return Get().cend(); }
constexpr auto crbegin() const requires(requires(const C& v) { v.crbegin(); }) { return Get().crbegin(); }
constexpr auto crend() const requires(requires(const C& v) { v.crend(); }) { return Get().crend(); }

constexpr bool empty() const requires(requires(const C& v) { v.empty(); }) { return Get().empty(); }
constexpr auto size() const requires(requires(const C& v) { v.size(); }) { return Get().size(); }
constexpr auto max_size() const requires(requires(const C& v) { v.max_size(); }) { return Get().max_size(); }
constexpr auto capacity() const requires(requires(const C& v) { v.capacity(); }) { return Get().capacity(); }

constexpr void clear() requires(requires(const C& v) { v.clear(); }) { Get().clear(); }
constexpr void reserve(size_type capacity) requires(requires(C& v) { v.reserve(std::declval<size_type>()); }) { Get().reserve(capacity); }
constexpr void resize(size_type size) requires(requires(C& v) { v.resize(std::declval<size_type>()); }) { Get().resize(size); }

constexpr auto& at(std::size_t pos) const requires(requires(const C& v) { v.at(0); }) { return Get().at(pos); }
constexpr auto& at(std::size_t pos) requires(requires(C& v) { v.at(0); }) { return Get().at(pos); }
constexpr auto& operator[](std::size_t pos) const requires(requires(const C& v) { v[pos]; }) { return at(pos); }
constexpr auto& operator[](std::size_t pos) requires(requires(C& v) { v[pos]; }) { return at(pos); }

constexpr void push_back(const value_type& arg)
requires(requires(C& v) { v.push_back(std::declval<const value_type&>()); }) {
Get().push_back(arg);
}

constexpr void push_back(value_type&& arg)
requires(requires(C& v) { v.push_back(std::declval<value_type>()); }) {
Get().push_back(std::move(arg));
}

constexpr void push_front(const value_type& arg)
requires(requires(C& v) { v.push_front(std::declval<const value_type&>()); }) {
Get().push_front(arg);
}

constexpr void push_front(value_type&& arg)
requires(requires(C& v) { v.push_front(std::declval<value_type>()); }) {
Get().push_front(std::move(arg));
}

template<typename... Args>
constexpr auto& emplace_back(Args&&... args)
requires(requires(C& v) { v.emplace_back(std::declval<Args>()...); }) {
return Get().emplace_back(std::forward<Args>(args)...);
}

template<typename... Args>
constexpr auto& emplace_front(Args&&... args)
requires(requires(C& v) { v.emplace_front(std::declval<Args>()...); }) {
return Get().emplace_front(std::forward<Args>(args)...);
}

constexpr auto& back() const requires(requires(const C& v) { v.back(); }) { return Get().back(); }
constexpr auto& back() requires(requires(C& v) { v.back(); }) { return Get().back(); }
constexpr auto& front() const requires(requires(const T& v) { v.front(); }) { return Get().front(); }
constexpr auto& front() requires(requires(C& v) { v.front(); }) { return Get().front(); }

constexpr void pop_back() requires(requires(C& v) { v.pop_back(); }) { Get().pop_back(); }
constexpr void pop_front() requires(requires(C& v) { v.pop_frontk(); }) { Get().pop_front(); }

auto operator<=>(const ContainerProxy& other) const
requires std::three_way_comparable<T>
{
return Get() <=> other.Get();
}

auto operator<=>(const C& other) const
requires std::three_way_comparable<T>
{
return Get() <=> other;
}

template<typename H>
friend H AbslHashValue(H hash, const ContainerProxy& proxy) {
return H::combine(std::move(hash), absl::HashOf<>(proxy.Get()));
}

// clang-format on
// NOLINTEND(readability-identifier-naming)
};

} // namespace mbo::types

namespace std {

template<typename T, typename R, std::remove_cvref_t<R>& (T::*F)(), const std::remove_cvref_t<R>& (T::*FC)() const>
requires(requires(const T& val) { std::hash<T>{}; })
struct hash<mbo::types::ContainerProxy<T, R, F, FC>> {
constexpr std::size_t operator()(const mbo::types::ContainerProxy<T, R, F, FC>& ptr) const noexcept {
return absl::HashOf<>(ptr);
}
};
} // namespace std

#endif // MBO_TYPES_CONTAINER_PROXY_H_
Loading

0 comments on commit 7bb5d0d

Please sign in to comment.