diff --git a/.github/ISSUE_TEMPLATE/00-bug_report.yml b/.github/ISSUE_TEMPLATE/00-bug_report.yml new file mode 100644 index 00000000000..32cebe15a8c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/00-bug_report.yml @@ -0,0 +1,53 @@ +name: Bug Report +description: Let us know that something does not work as expected. +title: "[Bug]: Please title this bug report" +body: + - type: textarea + id: what-happened + attributes: + label: Describe the issue + description: What happened, and what did you expect to happen? + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce the problem + description: It is important that we are able to reproduce the problem that you are experiencing. Please provide all code and relevant steps to reproduce the problem, including your `BUILD`/`CMakeLists.txt` file and build commands. Links to a GitHub branch or [godbolt.org](https://godbolt.org/) that demonstrate the problem are also helpful. + validations: + required: true + - type: textarea + id: version + attributes: + label: What version of Abseil are you using? + description: Please include the output of `git rev-parse HEAD` or the name of the LTS release that you are using. + validations: + required: true + - type: textarea + id: os + attributes: + label: What operating system and version are you using? + description: If you are using a Linux distribution please include the name and version of the distribution as well. + validations: + required: true + - type: textarea + id: compiler + attributes: + label: What compiler and version are you using? + description: Please include the output of `gcc -v` or `clang -v`, or the equivalent for your compiler. + validations: + required: true + - type: textarea + id: buildsystem + attributes: + label: What build system are you using? + description: Please include the output of `bazel --version` or `cmake --version`, or the equivalent for your build system. + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional context + description: Add any other context about the problem here. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..c690fd9a51b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Question + url: https://github.com/abseil/abseil-cpp/discussions + about: Have a question? Ask us anything! :-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..ff55e352945 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ +Thank you for your contribution to Abseil! + +Before submitting this PR, please be sure to read our [contributing +guidelines](https://github.com/abseil/abseil-cpp/blob/master/CONTRIBUTING.md). + +If you are a Googler, please also note that it is required that you send us a +Piper CL instead of using the GitHub pull-request process. The code propagation +process will deliver the change to GitHub. diff --git a/.gitignore b/.gitignore index d54fa5a91fe..3a729f416b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# Bzlmod lockfile +MODULE.bazel.lock # Ignore all bazel-* symlinks. /bazel-* # Ignore Bazel verbose explanations diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 00000000000..03122a958e5 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,35 @@ +# Copyright 2020 The Abseil Authors. +# +# 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 +# +# https://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. +# + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +# Expose license for external usage through bazel. +exports_files([ + "AUTHORS", + "LICENSE", +]) + +# For building with clang-cl. +# https://bazel.build/configure/windows#clang +platform( + name = "x64_windows-clang-cl", + constraint_values = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + "@bazel_tools//tools/cpp:clang-cl", + ], +) diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake new file mode 100644 index 00000000000..2e3a2f22830 --- /dev/null +++ b/CMake/AbseilDll.cmake @@ -0,0 +1,949 @@ +include(CMakeParseArguments) +include(GNUInstallDirs) + +set(ABSL_INTERNAL_DLL_FILES + "algorithm/algorithm.h" + "algorithm/container.h" + "base/attributes.h" + "base/call_once.h" + "base/casts.cc" + "base/casts.h" + "base/config.h" + "base/const_init.h" + "base/dynamic_annotations.h" + "base/fast_type_id.h" + "base/internal/atomic_hook.h" + "base/internal/cpu_detect.cc" + "base/internal/cpu_detect.h" + "base/internal/cycleclock.cc" + "base/internal/cycleclock.h" + "base/internal/cycleclock_config.h" + "base/internal/direct_mmap.h" + "base/internal/dynamic_annotations.h" + "base/internal/endian.h" + "base/internal/errno_saver.h" + "base/internal/hardening.h" + "base/internal/hide_ptr.h" + "base/internal/iterator_traits.h" + "base/internal/low_level_alloc.cc" + "base/internal/low_level_alloc.h" + "base/internal/low_level_scheduling.h" + "base/internal/nullability_traits.h" + "base/internal/per_thread_tls.h" + "base/internal/poison.cc" + "base/internal/poison.h" + "base/internal/pretty_function.h" + "base/internal/raw_logging.cc" + "base/internal/raw_logging.h" + "base/internal/scheduling_mode.h" + "base/internal/scoped_set_env.cc" + "base/internal/scoped_set_env.h" + "base/internal/spinlock.cc" + "base/internal/spinlock.h" + "base/internal/spinlock_wait.cc" + "base/internal/spinlock_wait.h" + "base/internal/strerror.cc" + "base/internal/strerror.h" + "base/internal/sysinfo.cc" + "base/internal/sysinfo.h" + "base/internal/thread_identity.cc" + "base/internal/thread_identity.h" + "base/internal/tracing.cc" + "base/internal/tracing.h" + "base/internal/tsan_mutex_interface.h" + "base/internal/unaligned_access.h" + "base/internal/unscaledcycleclock.cc" + "base/internal/unscaledcycleclock.h" + "base/internal/unscaledcycleclock_config.h" + "base/log_severity.cc" + "base/log_severity.h" + "base/macros.h" + "base/no_destructor.h" + "base/nullability.h" + "base/optimization.h" + "base/options.h" + "base/policy_checks.h" + "base/port.h" + "base/prefetch.h" + "base/thread_annotations.h" + "base/throw_delegate.cc" + "base/throw_delegate.h" + "cleanup/cleanup.h" + "cleanup/internal/cleanup.h" + "container/btree_map.h" + "container/btree_set.h" + "container/chunked_queue.h" + "container/fixed_array.h" + "container/flat_hash_map.h" + "container/flat_hash_set.h" + "container/hash_container_defaults.h" + "container/inlined_vector.h" + "container/internal/btree.h" + "container/internal/btree_container.h" + "container/internal/chunked_queue.h" + "container/internal/common.h" + "container/internal/common_policy_traits.h" + "container/internal/compressed_tuple.h" + "container/internal/container_memory.h" + "container/internal/hash_function_defaults.h" + "container/internal/hash_policy_traits.h" + "container/internal/hashtable_control_bytes.h" + "container/internal/hashtable_debug.h" + "container/internal/hashtable_debug_hooks.h" + "container/internal/hashtablez_sampler.cc" + "container/internal/hashtablez_sampler.h" + "container/internal/hashtablez_sampler_force_weak_definition.cc" + "container/internal/inlined_vector.h" + "container/internal/layout.h" + "container/internal/node_slot_policy.h" + "container/internal/raw_hash_map.h" + "container/internal/raw_hash_set.cc" + "container/internal/raw_hash_set.h" + "container/internal/raw_hash_set_resize_impl.h" + "container/internal/tracked.h" + "container/linked_hash_map.h" + "container/linked_hash_set.h" + "container/node_hash_map.h" + "container/node_hash_set.h" + "crc/crc32c.cc" + "crc/crc32c.h" + "crc/internal/crc.cc" + "crc/internal/crc.h" + "crc/internal/crc32_x86_arm_combined_simd.h" + "crc/internal/crc32c.h" + "crc/internal/crc32c_inline.h" + "crc/internal/crc_cord_state.cc" + "crc/internal/crc_cord_state.h" + "crc/internal/crc_internal.h" + "crc/internal/crc_memcpy.h" + "crc/internal/crc_memcpy_fallback.cc" + "crc/internal/crc_memcpy_x86_arm_combined.cc" + "crc/internal/crc_non_temporal_memcpy.cc" + "crc/internal/crc_x86_arm_combined.cc" + "crc/internal/non_temporal_arm_intrinsics.h" + "crc/internal/non_temporal_memcpy.h" + "debugging/failure_signal_handler.cc" + "debugging/failure_signal_handler.h" + "debugging/internal/address_is_readable.cc" + "debugging/internal/address_is_readable.h" + "debugging/internal/addresses.h" + "debugging/internal/bounded_utf8_length_sequence.h" + "debugging/internal/decode_rust_punycode.cc" + "debugging/internal/decode_rust_punycode.h" + "debugging/internal/demangle.cc" + "debugging/internal/demangle.h" + "debugging/internal/demangle_rust.cc" + "debugging/internal/demangle_rust.h" + "debugging/internal/elf_mem_image.cc" + "debugging/internal/elf_mem_image.h" + "debugging/internal/examine_stack.cc" + "debugging/internal/examine_stack.h" + "debugging/internal/stack_consumption.cc" + "debugging/internal/stack_consumption.h" + "debugging/internal/stacktrace_config.h" + "debugging/internal/symbolize.h" + "debugging/internal/utf8_for_code_point.cc" + "debugging/internal/utf8_for_code_point.h" + "debugging/internal/vdso_support.cc" + "debugging/internal/vdso_support.h" + "debugging/leak_check.h" + "debugging/stacktrace.cc" + "debugging/stacktrace.h" + "debugging/symbolize.cc" + "debugging/symbolize.h" + "functional/any_invocable.h" + "functional/bind_back.h" + "functional/bind_front.h" + "functional/function_ref.h" + "functional/internal/any_invocable.h" + "functional/internal/back_binder.h" + "functional/internal/front_binder.h" + "functional/internal/function_ref.h" + "functional/overload.h" + "hash/hash.h" + "hash/internal/city.cc" + "hash/internal/city.h" + "hash/internal/hash.cc" + "hash/internal/hash.h" + "hash/internal/spy_hash_state.h" + "hash/internal/weakly_mixed_integer.h" + "log/absl_check.h" + "log/absl_log.h" + "log/absl_vlog_is_on.h" + "log/check.h" + "log/die_if_null.cc" + "log/die_if_null.h" + "log/globals.cc" + "log/globals.h" + "log/initialize.cc" + "log/initialize.h" + "log/internal/append_truncated.h" + "log/internal/check_impl.h" + "log/internal/check_op.cc" + "log/internal/check_op.h" + "log/internal/conditions.cc" + "log/internal/conditions.h" + "log/internal/config.h" + "log/internal/container.h" + "log/internal/fnmatch.cc" + "log/internal/fnmatch.h" + "log/internal/globals.cc" + "log/internal/globals.h" + "log/internal/log_format.cc" + "log/internal/log_format.h" + "log/internal/log_impl.h" + "log/internal/log_message.cc" + "log/internal/log_message.h" + "log/internal/log_sink_set.cc" + "log/internal/log_sink_set.h" + "log/internal/nullguard.cc" + "log/internal/nullguard.h" + "log/internal/nullstream.h" + "log/internal/proto.cc" + "log/internal/proto.h" + "log/internal/strip.h" + "log/internal/structured.h" + "log/internal/structured_proto.cc" + "log/internal/structured_proto.h" + "log/internal/vlog_config.cc" + "log/internal/vlog_config.h" + "log/internal/voidify.h" + "log/log.h" + "log/log_entry.cc" + "log/log_entry.h" + "log/log_sink.cc" + "log/log_sink.h" + "log/log_sink_registry.h" + "log/log_streamer.h" + "log/structured.h" + "log/vlog_is_on.h" + "memory/memory.h" + "meta/internal/requires.h" + "meta/type_traits.h" + "numeric/bits.h" + "numeric/int128.cc" + "numeric/int128.h" + "numeric/internal/bits.h" + "numeric/internal/representation.h" + "profiling/hashtable.cc" + "profiling/hashtable.h" + "profiling/internal/exponential_biased.cc" + "profiling/internal/exponential_biased.h" + "profiling/internal/periodic_sampler.cc" + "profiling/internal/periodic_sampler.h" + "profiling/internal/profile_builder.cc" + "profiling/internal/profile_builder.h" + "profiling/internal/sample_recorder.h" + "random/bernoulli_distribution.h" + "random/beta_distribution.h" + "random/bit_gen_ref.h" + "random/discrete_distribution.cc" + "random/discrete_distribution.h" + "random/distributions.h" + "random/exponential_distribution.h" + "random/gaussian_distribution.cc" + "random/gaussian_distribution.h" + "random/internal/distribution_caller.h" + "random/internal/entropy_pool.cc" + "random/internal/entropy_pool.h" + "random/internal/fast_uniform_bits.h" + "random/internal/fastmath.h" + "random/internal/generate_real.h" + "random/internal/iostream_state_saver.h" + "random/internal/nonsecure_base.h" + "random/internal/pcg_engine.h" + "random/internal/platform.h" + "random/internal/randen.cc" + "random/internal/randen.h" + "random/internal/randen_detect.cc" + "random/internal/randen_detect.h" + "random/internal/randen_engine.h" + "random/internal/randen_hwaes.cc" + "random/internal/randen_hwaes.h" + "random/internal/randen_round_keys.cc" + "random/internal/randen_slow.cc" + "random/internal/randen_slow.h" + "random/internal/randen_traits.h" + "random/internal/salted_seed_seq.h" + "random/internal/seed_material.cc" + "random/internal/seed_material.h" + "random/internal/sequence_urbg.h" + "random/internal/traits.h" + "random/internal/uniform_helper.h" + "random/internal/wide_multiply.h" + "random/log_uniform_int_distribution.h" + "random/poisson_distribution.h" + "random/random.h" + "random/seed_gen_exception.cc" + "random/seed_gen_exception.h" + "random/seed_sequences.cc" + "random/seed_sequences.h" + "random/uniform_int_distribution.h" + "random/uniform_real_distribution.h" + "random/zipf_distribution.h" + "status/internal/status_internal.cc" + "status/internal/status_internal.h" + "status/internal/statusor_internal.h" + "status/status.cc" + "status/status.h" + "status/status_builder.cc" + "status/status_builder.h" + "status/status_macros.h" + "status/status_payload_printer.cc" + "status/status_payload_printer.h" + "status/statusor.cc" + "status/statusor.h" + "strings/ascii.cc" + "strings/ascii.h" + "strings/charconv.cc" + "strings/charconv.h" + "strings/charset.h" + "strings/cord.cc" + "strings/cord.h" + "strings/cord_analysis.cc" + "strings/cord_analysis.h" + "strings/cord_buffer.h" + "strings/escaping.cc" + "strings/escaping.h" + "strings/has_absl_stringify.h" + "strings/has_ostream_operator.h" + "strings/internal/append_and_overwrite.h" + "strings/internal/charconv_bigint.cc" + "strings/internal/charconv_bigint.h" + "strings/internal/charconv_parse.cc" + "strings/internal/charconv_parse.h" + "strings/internal/cord_data_edge.h" + "strings/internal/cord_internal.cc" + "strings/internal/cord_internal.h" + "strings/internal/cord_rep_btree.cc" + "strings/internal/cord_rep_btree.h" + "strings/internal/cord_rep_btree_navigator.cc" + "strings/internal/cord_rep_btree_navigator.h" + "strings/internal/cord_rep_btree_reader.cc" + "strings/internal/cord_rep_btree_reader.h" + "strings/internal/cord_rep_consume.cc" + "strings/internal/cord_rep_consume.h" + "strings/internal/cord_rep_crc.cc" + "strings/internal/cord_rep_crc.h" + "strings/internal/cord_rep_flat.h" + "strings/internal/cordz_functions.cc" + "strings/internal/cordz_functions.h" + "strings/internal/cordz_handle.cc" + "strings/internal/cordz_handle.h" + "strings/internal/cordz_info.cc" + "strings/internal/cordz_info.h" + "strings/internal/cordz_sample_token.cc" + "strings/internal/cordz_sample_token.h" + "strings/internal/cordz_statistics.h" + "strings/internal/cordz_update_scope.h" + "strings/internal/cordz_update_tracker.h" + "strings/internal/damerau_levenshtein_distance.cc" + "strings/internal/damerau_levenshtein_distance.h" + "strings/internal/escaping.cc" + "strings/internal/escaping.h" + "strings/internal/generic_printer.cc" + "strings/internal/generic_printer.h" + "strings/internal/generic_printer_internal.h" + "strings/internal/memutil.cc" + "strings/internal/memutil.h" + "strings/internal/ostringstream.cc" + "strings/internal/ostringstream.h" + "strings/internal/pow10_helper.cc" + "strings/internal/pow10_helper.h" + "strings/internal/resize_uninitialized.h" + "strings/internal/str_format/arg.cc" + "strings/internal/str_format/arg.h" + "strings/internal/str_format/bind.cc" + "strings/internal/str_format/bind.h" + "strings/internal/str_format/checker.h" + "strings/internal/str_format/constexpr_parser.h" + "strings/internal/str_format/extension.cc" + "strings/internal/str_format/extension.h" + "strings/internal/str_format/float_conversion.cc" + "strings/internal/str_format/float_conversion.h" + "strings/internal/str_format/output.cc" + "strings/internal/str_format/output.h" + "strings/internal/str_format/parser.cc" + "strings/internal/str_format/parser.h" + "strings/internal/str_join_internal.h" + "strings/internal/str_split_internal.h" + "strings/internal/string_constant.h" + "strings/internal/stringify_sink.cc" + "strings/internal/stringify_sink.h" + "strings/internal/stringify_stream.h" + "strings/internal/utf8.cc" + "strings/internal/utf8.h" + "strings/match.cc" + "strings/match.h" + "strings/numbers.cc" + "strings/numbers.h" + "strings/resize_and_overwrite.h" + "strings/str_cat.cc" + "strings/str_cat.h" + "strings/str_format.h" + "strings/str_join.h" + "strings/str_replace.cc" + "strings/str_replace.h" + "strings/str_split.cc" + "strings/str_split.h" + "strings/strip.h" + "strings/substitute.cc" + "strings/substitute.h" + "synchronization/barrier.cc" + "synchronization/barrier.h" + "synchronization/blocking_counter.cc" + "synchronization/blocking_counter.h" + "synchronization/internal/create_thread_identity.cc" + "synchronization/internal/create_thread_identity.h" + "synchronization/internal/futex.h" + "synchronization/internal/futex_waiter.cc" + "synchronization/internal/futex_waiter.h" + "synchronization/internal/graphcycles.cc" + "synchronization/internal/graphcycles.h" + "synchronization/internal/kernel_timeout.cc" + "synchronization/internal/kernel_timeout.h" + "synchronization/internal/per_thread_sem.cc" + "synchronization/internal/per_thread_sem.h" + "synchronization/internal/pthread_waiter.cc" + "synchronization/internal/pthread_waiter.h" + "synchronization/internal/sem_waiter.cc" + "synchronization/internal/sem_waiter.h" + "synchronization/internal/stdcpp_waiter.cc" + "synchronization/internal/stdcpp_waiter.h" + "synchronization/internal/thread_pool.h" + "synchronization/internal/waiter.h" + "synchronization/internal/waiter_base.cc" + "synchronization/internal/waiter_base.h" + "synchronization/internal/win32_waiter.cc" + "synchronization/internal/win32_waiter.h" + "synchronization/mutex.cc" + "synchronization/mutex.h" + "synchronization/notification.cc" + "synchronization/notification.h" + "time/civil_time.cc" + "time/civil_time.h" + "time/clock.cc" + "time/clock.h" + "time/clock_interface.cc" + "time/clock_interface.h" + "time/duration.cc" + "time/format.cc" + "time/internal/cctz/include/cctz/civil_time.h" + "time/internal/cctz/include/cctz/civil_time_detail.h" + "time/internal/cctz/include/cctz/time_zone.h" + "time/internal/cctz/include/cctz/zone_info_source.h" + "time/internal/cctz/src/civil_time_detail.cc" + "time/internal/cctz/src/time_zone_fixed.cc" + "time/internal/cctz/src/time_zone_fixed.h" + "time/internal/cctz/src/time_zone_format.cc" + "time/internal/cctz/src/time_zone_if.cc" + "time/internal/cctz/src/time_zone_if.h" + "time/internal/cctz/src/time_zone_impl.cc" + "time/internal/cctz/src/time_zone_impl.h" + "time/internal/cctz/src/time_zone_info.cc" + "time/internal/cctz/src/time_zone_info.h" + "time/internal/cctz/src/time_zone_libc.cc" + "time/internal/cctz/src/time_zone_libc.h" + "time/internal/cctz/src/time_zone_lookup.cc" + "time/internal/cctz/src/time_zone_posix.cc" + "time/internal/cctz/src/time_zone_posix.h" + "time/internal/cctz/src/tzfile.h" + "time/internal/cctz/src/zone_info_source.cc" + "time/simulated_clock.cc" + "time/simulated_clock.h" + "time/time.cc" + "time/time.h" + "types/any.h" + "types/any_span.h" + "types/compare.h" + "types/internal/any_span.h" + "types/internal/span.h" + "types/optional.h" + "types/optional_ref.h" + "types/source_location.cc" + "types/source_location.h" + "types/span.h" + "types/variant.h" + "utility/utility.h" + "debugging/leak_check.cc" + "strings/string_view.h" +) + +if(WIN32) + list(APPEND ABSL_INTERNAL_DLL_FILES + "time/internal/cctz/src/time_zone_name_win.cc" + "time/internal/cctz/src/time_zone_name_win.h" + ) +endif() + +if(NOT MSVC) + list(APPEND ABSL_INTERNAL_DLL_FILES + "flags/commandlineflag.cc" + "flags/commandlineflag.h" + "flags/config.h" + "flags/declare.h" + "flags/flag.h" + "flags/internal/commandlineflag.cc" + "flags/internal/commandlineflag.h" + "flags/internal/flag.cc" + "flags/internal/flag.h" + "flags/internal/parse.h" + "flags/internal/path_util.h" + "flags/internal/private_handle_accessor.cc" + "flags/internal/private_handle_accessor.h" + "flags/internal/program_name.cc" + "flags/internal/program_name.h" + "flags/internal/registry.h" + "flags/internal/sequence_lock.h" + "flags/internal/usage.cc" + "flags/internal/usage.h" + "flags/marshalling.cc" + "flags/marshalling.h" + "flags/parse.cc" + "flags/parse.h" + "flags/reflection.cc" + "flags/reflection.h" + "flags/usage.cc" + "flags/usage.h" + "flags/usage_config.cc" + "flags/usage_config.h" + "log/flags.cc" + "log/flags.h" + "log/internal/flags.h" + ) +endif() + +set(ABSL_INTERNAL_DLL_TARGETS + "absl_check" + "absl_log" + "absl_vlog_is_on" + "algorithm" + "algorithm_container" + "any" + "any_invocable" + "atomic_hook" + "base" + "base_internal" + "bind_front" + "bits" + "btree" + "check" + "city" + "civil_time" + "compare" + "compressed_tuple" + "config" + "container" + "container_common" + "container_memory" + "cord" + "cord_internal" + "cordz_functions" + "cordz_handle" + "cordz_info" + "cordz_sample_token" + "core_headers" + "counting_allocator" + "crc_cord_state" + "crc_cpu_detect" + "crc_internal" + "crc32c" + "debugging" + "debugging_internal" + "demangle_internal" + "die_if_null" + "dynamic_annotations" + "endian" + "examine_stack" + "exponential_biased" + "failure_signal_handler" + "fixed_array" + "flat_hash_map" + "flat_hash_set" + "function_ref" + "graphcycles_internal" + "hash" + "hash_function_defaults" + "hash_policy_traits" + "hashtable_debug" + "hashtable_debug_hooks" + "hashtablez_sampler" + "inlined_vector" + "inlined_vector_internal" + "int128" + "kernel_timeout_internal" + "layout" + "leak_check" + "log_internal_check_impl" + "log_internal_check_op" + "log_internal_conditions" + "log_internal_config" + "log_internal_fnmatch" + "log_internal_format" + "log_internal_globals" + "log_internal_log_impl" + "log_internal_proto" + "log_internal_message" + "log_internal_log_sink_set" + "log_internal_nullguard" + "log_internal_nullstream" + "log_internal_strip" + "log_internal_voidify" + "log_internal_append_truncated" + "log_globals" + "log_initialize" + "log" + "log_entry" + "log_sink" + "log_sink_registry" + "log_streamer" + "log_internal_structured" + "log_severity" + "log_structured" + "low_level_hash" + "malloc_internal" + "memory" + "meta" + "node_hash_map" + "node_hash_set" + "node_slot_policy" + "non_temporal_arm_intrinsics" + "non_temporal_memcpy" + "numeric" + "optional" + "periodic_sampler" + "pow10_helper" + "pretty_function" + "random_bit_gen_ref" + "random_distributions" + "random_internal_distribution_caller" + "random_internal_distributions" + "random_internal_explicit_seed_seq" + "random_internal_fastmath" + "random_internal_fast_uniform_bits" + "random_internal_generate_real" + "random_internal_iostream_state_saver" + "random_internal_nonsecure_base" + "random_internal_pcg_engine" + "random_internal_platform" + "random_internal_pool_urbg" + "random_internal_randen" + "random_internal_randen_engine" + "random_internal_randen_hwaes" + "random_internal_randen_hwaes_impl" + "random_internal_randen_slow" + "random_internal_salted_seed_seq" + "random_internal_seed_material" + "random_internal_sequence_urbg" + "random_internal_traits" + "random_internal_uniform_helper" + "random_internal_wide_multiply" + "random_random" + "random_seed_gen_exception" + "random_seed_sequences" + "raw_hash_map" + "raw_hash_set" + "raw_logging_internal" + "sample_recorder" + "scoped_set_env" + "span" + "spinlock_wait" + "spy_hash_state" + "stack_consumption" + "stacktrace" + "status" + "statusor" + "str_format" + "str_format_internal" + "strerror" + "strings" + "strings_internal" + "string_view" + "symbolize" + "synchronization" + "thread_pool" + "throw_delegate" + "time" + "time_zone" + "tracked" + "type_traits" + "utility" + "variant" + "vlog_config_internal" + "vlog_is_on" +) + +if(NOT MSVC) + list(APPEND ABSL_INTERNAL_DLL_TARGETS + "flags" + "flags_commandlineflag" + "flags_commandlineflag_internal" + "flags_config" + "flags_internal" + "flags_marshalling" + "flags_parse" + "flags_path_util" + "flags_private_handle_accessor" + "flags_program_name" + "flags_reflection" + "flags_usage" + "flags_usage_internal" + "log_internal_flags" + "log_flags" + ) +endif() + +set(ABSL_INTERNAL_TEST_DLL_FILES + "hash/hash_testing.h" + "log/scoped_mock_log.cc" + "log/scoped_mock_log.h" + "random/internal/chi_square.cc" + "random/internal/chi_square.h" + "random/internal/distribution_test_util.cc" + "random/internal/distribution_test_util.h" + "random/internal/mock_helpers.h" + "random/internal/mock_overload_set.h" + "random/mocking_bit_gen.h" + "random/mock_distributions.h" + "status/status_matchers.h" + "status/internal/status_matchers.cc" + "status/internal/status_matchers.h" + "strings/cordz_test_helpers.h" + "strings/cord_test_helpers.h" +) + +set(ABSL_INTERNAL_TEST_DLL_TARGETS + "cord_test_helpers" + "cordz_test_helpers" + "hash_testing" + "random_mocking_bit_gen" + "random_internal_distribution_test_util" + "random_internal_mock_overload_set" + "scoped_mock_log" + "status_matchers" +) + +if(DEFINED CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD_REQUIRED) + if(CMAKE_CXX_STANDARD GREATER_EQUAL 20) + set(ABSL_INTERNAL_AT_LEAST_CXX20 ON) + set(ABSL_INTERNAL_AT_LEAST_CXX17 ON) + elseif(CMAKE_CXX_STANDARD GREATER_EQUAL 17) + set(ABSL_INTERNAL_AT_LEAST_CXX17 ON) + endif() +else() + include(CheckCXXSourceCompiles) + + check_cxx_source_compiles( + [==[ +#ifdef _MSC_VER +# if _MSVC_LANG < 201703L +# error "The compiler defaults or is configured for C++ < 17" +# endif +#elif __cplusplus < 201703L +# error "The compiler defaults or is configured for C++ < 17" +#endif +int main() { return 0; } +]==] + ABSL_INTERNAL_AT_LEAST_CXX17) + + check_cxx_source_compiles( + [==[ +#ifdef _MSC_VER +# if _MSVC_LANG < 202002L +# error "The compiler defaults or is configured for C++ < 20" +# endif +#elif __cplusplus < 202002L +# error "The compiler defaults or is configured for C++ < 20" +#endif +int main() { return 0; } +]==] + ABSL_INTERNAL_AT_LEAST_CXX20) +endif() + +if(ABSL_INTERNAL_AT_LEAST_CXX20) + set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_20) +elseif(ABSL_INTERNAL_AT_LEAST_CXX17) + set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_17) +else() + message(FATAL_ERROR "The compiler defaults to or is configured for C++ < 17. C++ >= 17 is required and Abseil and all libraries that use Abseil must use the same C++ language standard") +endif() + +function(absl_internal_dll_contains) + cmake_parse_arguments(ABSL_INTERNAL_DLL + "" + "OUTPUT;TARGET" + "" + ${ARGN} + ) + + STRING(REGEX REPLACE "^absl::" "" _target ${ABSL_INTERNAL_DLL_TARGET}) + + if (_target IN_LIST ABSL_INTERNAL_DLL_TARGETS) + set(${ABSL_INTERNAL_DLL_OUTPUT} 1 PARENT_SCOPE) + else() + set(${ABSL_INTERNAL_DLL_OUTPUT} 0 PARENT_SCOPE) + endif() +endfunction() + +function(absl_internal_test_dll_contains) + cmake_parse_arguments(ABSL_INTERNAL_TEST_DLL + "" + "OUTPUT;TARGET" + "" + ${ARGN} + ) + + STRING(REGEX REPLACE "^absl::" "" _target ${ABSL_INTERNAL_TEST_DLL_TARGET}) + + if (_target IN_LIST ABSL_INTERNAL_TEST_DLL_TARGETS) + set(${ABSL_INTERNAL_TEST_DLL_OUTPUT} 1 PARENT_SCOPE) + else() + set(${ABSL_INTERNAL_TEST_DLL_OUTPUT} 0 PARENT_SCOPE) + endif() +endfunction() + +function(absl_internal_dll_targets) + cmake_parse_arguments(ABSL_INTERNAL_DLL + "" + "OUTPUT" + "DEPS" + ${ARGN} + ) + + set(_deps "") + foreach(dep IN LISTS ABSL_INTERNAL_DLL_DEPS) + absl_internal_dll_contains(TARGET ${dep} OUTPUT _dll_contains) + absl_internal_test_dll_contains(TARGET ${dep} OUTPUT _test_dll_contains) + if (_dll_contains) + list(APPEND _deps abseil_dll) + elseif (_test_dll_contains) + list(APPEND _deps abseil_test_dll) + else() + list(APPEND _deps ${dep}) + endif() + endforeach() + + # Because we may have added the DLL multiple times + list(REMOVE_DUPLICATES _deps) + set(${ABSL_INTERNAL_DLL_OUTPUT} "${_deps}" PARENT_SCOPE) +endfunction() + +function(absl_make_dll) + cmake_parse_arguments(ABSL_INTERNAL_MAKE_DLL + "" + "TEST" + "" + ${ARGN} + ) + + if (ABSL_INTERNAL_MAKE_DLL_TEST) + set(_dll "abseil_test_dll") + set(_dll_files ${ABSL_INTERNAL_TEST_DLL_FILES}) + set(_dll_libs "abseil_dll" "GTest::gtest" "GTest::gmock") + set(_dll_compile_definitions "GTEST_LINKED_AS_SHARED_LIBRARY=1") + set(_dll_includes ${absl_gtest_src_dir}/googletest/include ${absl_gtest_src_dir}/googlemock/include) + set(_dll_consume "ABSL_CONSUME_TEST_DLL") + set(_dll_build "ABSL_BUILD_TEST_DLL") + else() + set(_dll "abseil_dll") + set(_dll_files ${ABSL_INTERNAL_DLL_FILES}) + set(_dll_libs + Threads::Threads + # TODO(#1495): Use $ once our + # minimum CMake version >= 3.24 + $<$:-Wl,-framework,CoreFoundation> + ) + set(_dll_compile_definitions "") + set(_dll_includes "") + set(_dll_consume "ABSL_CONSUME_DLL") + set(_dll_build "ABSL_BUILD_DLL") + endif() + + add_library( + ${_dll} + SHARED + ${_dll_files} + ) + target_link_libraries( + ${_dll} + PRIVATE + ${_dll_libs} + ${ABSL_DEFAULT_LINKOPTS} + $<$:-llog> + $<$:-ladvapi32> + $<$:-ldbghelp> + $<$:-lbcrypt> + ) + set_target_properties(${_dll} PROPERTIES + LINKER_LANGUAGE "CXX" + SOVERSION ${ABSL_SOVERSION} + ) + target_include_directories( + ${_dll} + PUBLIC + "$" + $ + PRIVATE + ${_dll_includes} + ) + + target_compile_options( + ${_dll} + PRIVATE + ${ABSL_DEFAULT_COPTS} + ) + + foreach(cflag ${ABSL_CC_LIB_COPTS}) + if(${cflag} MATCHES "^(-Wno|/wd)") + # These flags are needed to suppress warnings that might fire in our headers. + set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") + elseif(${cflag} MATCHES "^(-W|/w[1234eo])") + # Don't impose our warnings on others. + else() + set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") + endif() + endforeach() + string(REPLACE ";" " " PC_LINKOPTS "${ABSL_CC_LIB_LINKOPTS}") + + FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/${_dll}.pc" CONTENT "\ +prefix=${CMAKE_INSTALL_PREFIX}\n\ +exec_prefix=\${prefix}\n\ +libdir=${CMAKE_INSTALL_FULL_LIBDIR}\n\ +includedir=${CMAKE_INSTALL_FULL_INCLUDEDIR}\n\ +\n\ +Name: ${_dll}\n\ +Description: Abseil DLL library\n\ +URL: https://abseil.io/\n\ +Version: ${absl_VERSION}\n\ +Libs: -L\${libdir} $<$>:-l${_dll}> ${PC_LINKOPTS}\n\ +Cflags: -I\${includedir}${PC_CFLAGS}\n") + INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/${_dll}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + + target_compile_definitions( + ${_dll} + PUBLIC + ${_dll_compile_definitions} + PRIVATE + ${_dll_build} + NOMINMAX + INTERFACE + ${ABSL_CC_LIB_DEFINES} + ${_dll_consume} + ) + + if(ABSL_PROPAGATE_CXX_STD) + # Abseil libraries require C++17 as the current minimum standard. When + # compiled with a higher minimum (either because it is the compiler's + # default or explicitly requested), then Abseil requires that standard. + target_compile_features(${_dll} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE}) + endif() + + install(TARGETS ${_dll} EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + + add_library(absl::${_dll} ALIAS ${_dll}) +endfunction() diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 67b97fe14df..61e1ae42116 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -16,13 +16,21 @@ include(CMakeParseArguments) include(AbseilConfigureCopts) -include(AbseilInstallDirs) +include(AbseilDll) # The IDE folder for Abseil that will be used if Abseil is included in a CMake # project that sets # set_property(GLOBAL PROPERTY USE_FOLDERS ON) # For example, Visual Studio supports folders. -set(ABSL_IDE_FOLDER Abseil) +if(NOT DEFINED ABSL_IDE_FOLDER) + set(ABSL_IDE_FOLDER Abseil) +endif() + +if(ABSL_USE_SYSTEM_INCLUDES) + set(ABSL_INTERNAL_INCLUDE_WARNING_GUARD SYSTEM) +else() + set(ABSL_INTERNAL_INCLUDE_WARNING_GUARD "") +endif() # absl_cc_library() # @@ -38,7 +46,8 @@ set(ABSL_IDE_FOLDER Abseil) # LINKOPTS: List of link options # PUBLIC: Add this so that this library will be exported under absl:: # Also in IDE, target will appear in Abseil folder while non PUBLIC will be in Abseil/internal. -# TESTONLY: When added, this target will only be built if user passes -DABSL_RUN_TESTS=ON to CMake. +# TESTONLY: When added, this target will only be built if both +# BUILD_TESTING=ON and ABSL_BUILD_TESTING=ON. # # Note: # By default, absl_cc_library will always create a library named absl_${NAME}, @@ -71,7 +80,7 @@ set(ABSL_IDE_FOLDER Abseil) # absl::fantastic_lib # ) # -# TODO: Implement "ALWAYSLINK" +# TODO(b/320467376): Implement "ALWAYSLINK". function(absl_cc_library) cmake_parse_arguments(ABSL_CC_LIB "DISABLE_INSTALL;PUBLIC;TESTONLY" @@ -80,95 +89,276 @@ function(absl_cc_library) ${ARGN} ) - if(NOT ABSL_CC_LIB_TESTONLY OR ABSL_RUN_TESTS) + if(ABSL_CC_LIB_TESTONLY AND + NOT ((BUILD_TESTING AND ABSL_BUILD_TESTING) OR + (ABSL_BUILD_TEST_HELPERS AND ABSL_CC_LIB_PUBLIC))) + return() + endif() + + if(ABSL_ENABLE_INSTALL) + set(_NAME "${ABSL_CC_LIB_NAME}") + else() + set(_NAME "absl_${ABSL_CC_LIB_NAME}") + endif() + + # Check if this is a header-only library + # Note that as of February 2019, many popular OS's (for example, Ubuntu + # 16.04 LTS) only come with cmake 3.5 by default. For this reason, we can't + # use list(FILTER...) + set(ABSL_CC_SRCS "${ABSL_CC_LIB_SRCS}") + foreach(src_file IN LISTS ABSL_CC_SRCS) + if(${src_file} MATCHES ".*\\.(h|inc)") + list(REMOVE_ITEM ABSL_CC_SRCS "${src_file}") + endif() + endforeach() + + if(ABSL_CC_SRCS STREQUAL "") + set(ABSL_CC_LIB_IS_INTERFACE 1) + else() + set(ABSL_CC_LIB_IS_INTERFACE 0) + endif() + + # Determine this build target's relationship to the DLL. It's one of four things: + # 1. "dll" -- This target is part of the DLL + # 2. "dll_dep" -- This target is not part of the DLL, but depends on the DLL. + # Note that we assume any target not in the DLL depends on the + # DLL. This is not a technical necessity but a convenience + # which happens to be true, because nearly every target is + # part of the DLL. + # 3. "shared" -- This is a shared library, perhaps on a non-windows platform + # where DLL doesn't make sense. + # 4. "static" -- This target does not depend on the DLL and should be built + # statically. + if (${ABSL_BUILD_DLL}) if(ABSL_ENABLE_INSTALL) - set(_NAME "${ABSL_CC_LIB_NAME}") + absl_internal_dll_contains(TARGET ${_NAME} OUTPUT _in_dll) + absl_internal_test_dll_contains(TARGET ${_NAME} OUTPUT _in_test_dll) else() - set(_NAME "absl_${ABSL_CC_LIB_NAME}") + absl_internal_dll_contains(TARGET ${ABSL_CC_LIB_NAME} OUTPUT _in_dll) + absl_internal_test_dll_contains(TARGET ${ABSL_CC_LIB_NAME} OUTPUT _in_test_dll) endif() - - # Check if this is a header-only library - # Note that as of February 2019, many popular OS's (for example, Ubuntu - # 16.04 LTS) only come with cmake 3.5 by default. For this reason, we can't - # use list(FILTER...) - set(ABSL_CC_SRCS "${ABSL_CC_LIB_SRCS}") - foreach(src_file IN LISTS ABSL_CC_SRCS) - if(${src_file} MATCHES ".*\\.(h|inc)") - list(REMOVE_ITEM ABSL_CC_SRCS "${src_file}") - endif() - endforeach() - if("${ABSL_CC_SRCS}" STREQUAL "") + if (${_in_dll} OR ${_in_test_dll}) + # This target should be replaced by the DLL + set(_build_type "dll") set(ABSL_CC_LIB_IS_INTERFACE 1) else() - set(ABSL_CC_LIB_IS_INTERFACE 0) + # Building a DLL, but this target is not part of the DLL + set(_build_type "dll_dep") + endif() + elseif(BUILD_SHARED_LIBS) + set(_build_type "shared") + else() + set(_build_type "static") + endif() + + # Generate a pkg-config file for every library: + if(ABSL_ENABLE_INSTALL) + if(absl_VERSION) + set(PC_VERSION "${absl_VERSION}") + else() + set(PC_VERSION "head") + endif() + if(NOT _build_type STREQUAL "dll") + set(LNK_LIB "${LNK_LIB} -labsl_${_NAME}") endif() + foreach(dep ${ABSL_CC_LIB_DEPS}) + if(${dep} MATCHES "^absl::(.*)") + # for DLL builds many libs are not created, but add + # the pkgconfigs nevertheless, pointing to the dll. + if(_build_type STREQUAL "dll") + # hide this MATCHES in an if-clause so it doesn't overwrite + # the CMAKE_MATCH_1 from (${dep} MATCHES "^absl::(.*)") + if(NOT PC_DEPS MATCHES "abseil_dll") + # Join deps with commas. + if(PC_DEPS) + set(PC_DEPS "${PC_DEPS},") + endif() + # don't duplicate dll-dep if it exists already + set(PC_DEPS "${PC_DEPS} abseil_dll = ${PC_VERSION}") + set(LNK_LIB "${LNK_LIB} -labseil_dll") + endif() + else() + # Join deps with commas. + if(PC_DEPS) + set(PC_DEPS "${PC_DEPS},") + endif() + set(PC_DEPS "${PC_DEPS} absl_${CMAKE_MATCH_1} = ${PC_VERSION}") + endif() + endif() + endforeach() + foreach(cflag ${ABSL_CC_LIB_COPTS}) + # Strip out the CMake-specific `SHELL:` prefix, which is used to construct + # a group of space-separated options. + # https://cmake.org/cmake/help/v3.30/command/target_compile_options.html#option-de-duplication + string(REGEX REPLACE "^SHELL:" "" cflag "${cflag}") + if(${cflag} MATCHES "^-Xarch_") + # An -Xarch_ flag implies that its successor only applies to the + # specified platform. Such option groups are each specified in a single + # `SHELL:`-prefixed string in the COPTS list, which we simply ignore. + elseif(${cflag} MATCHES "^(-Wno-|/wd)") + # These flags are needed to suppress warnings that might fire in our headers. + set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") + elseif(${cflag} MATCHES "^(-W|/w[1234eo])") + # Don't impose our warnings on others. + elseif(${cflag} MATCHES "^-m") + # Don't impose CPU instruction requirements on others, as + # the code performs feature detection on runtime. + else() + set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") + endif() + endforeach() + string(REPLACE ";" " " PC_LINKOPTS "${ABSL_CC_LIB_LINKOPTS}") + FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\ +prefix=${CMAKE_INSTALL_PREFIX}\n\ +exec_prefix=\${prefix}\n\ +libdir=${CMAKE_INSTALL_FULL_LIBDIR}\n\ +includedir=${CMAKE_INSTALL_FULL_INCLUDEDIR}\n\ +\n\ +Name: absl_${_NAME}\n\ +Description: Abseil ${_NAME} library\n\ +URL: https://abseil.io/\n\ +Version: ${PC_VERSION}\n\ +Requires:${PC_DEPS}\n\ +Libs: -L\${libdir} $<$>:${LNK_LIB}> ${PC_LINKOPTS}\n\ +Cflags: -I\${includedir}${PC_CFLAGS}\n") + INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + endif() - if(NOT ABSL_CC_LIB_IS_INTERFACE) + if(NOT ABSL_CC_LIB_IS_INTERFACE) + if(_build_type STREQUAL "dll_dep") + # This target depends on the DLL. When adding dependencies to this target, + # any depended-on-target which is contained inside the DLL is replaced + # with a dependency on the DLL. add_library(${_NAME} STATIC "") target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS}) - target_include_directories(${_NAME} - PUBLIC - "$" - $ + absl_internal_dll_targets( + DEPS ${ABSL_CC_LIB_DEPS} + OUTPUT _dll_deps ) - target_compile_options(${_NAME} - PRIVATE ${ABSL_CC_LIB_COPTS}) target_link_libraries(${_NAME} - PUBLIC ${ABSL_CC_LIB_DEPS} + PUBLIC ${_dll_deps} PRIVATE ${ABSL_CC_LIB_LINKOPTS} ${ABSL_DEFAULT_LINKOPTS} ) - target_compile_definitions(${_NAME} PUBLIC ${ABSL_CC_LIB_DEFINES}) - # Add all Abseil targets to a a folder in the IDE for organization. - if(ABSL_CC_LIB_PUBLIC) - set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}) - elseif(ABSL_CC_LIB_TESTONLY) - set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test) + if (ABSL_CC_LIB_TESTONLY) + set(_gtest_link_define "GTEST_LINKED_AS_SHARED_LIBRARY=1") else() - set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/internal) + set(_gtest_link_define) + endif() + + target_compile_definitions(${_NAME} + PUBLIC + ABSL_CONSUME_DLL + "${_gtest_link_define}" + ) + + elseif(_build_type STREQUAL "static" OR _build_type STREQUAL "shared") + add_library(${_NAME} "") + target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS}) + if(APPLE) + set_target_properties(${_NAME} PROPERTIES + INSTALL_RPATH "@loader_path") + elseif(UNIX) + set_target_properties(${_NAME} PROPERTIES + INSTALL_RPATH "$ORIGIN") endif() + target_link_libraries(${_NAME} + PUBLIC ${ABSL_CC_LIB_DEPS} + PRIVATE + ${ABSL_CC_LIB_LINKOPTS} + ${ABSL_DEFAULT_LINKOPTS} + ) + else() + message(FATAL_ERROR "Invalid build type: ${_build_type}") + endif() + + # Linker language can be inferred from sources, but in the case of DLLs we + # don't have any .cc files so it would be ambiguous. We could set it + # explicitly only in the case of DLLs but, because "CXX" is always the + # correct linker language for static or for shared libraries, we set it + # unconditionally. + set_property(TARGET ${_NAME} PROPERTY LINKER_LANGUAGE "CXX") + + target_include_directories(${_NAME} ${ABSL_INTERNAL_INCLUDE_WARNING_GUARD} + PUBLIC + "$" + $ + ) + target_compile_options(${_NAME} + PRIVATE ${ABSL_CC_LIB_COPTS}) + target_compile_definitions(${_NAME} PUBLIC ${ABSL_CC_LIB_DEFINES}) + + # Add all Abseil targets to a a folder in the IDE for organization. + if(ABSL_CC_LIB_PUBLIC) + set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}) + elseif(ABSL_CC_LIB_TESTONLY) + set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test) + else() + set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/internal) + endif() - # INTERFACE libraries can't have the CXX_STANDARD property set - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + if(ABSL_PROPAGATE_CXX_STD) + # Abseil libraries require C++17 as the current minimum standard. When + # compiled with a higher standard (either because it is the compiler's + # default or explicitly requested), then Abseil requires that standard. + target_compile_features(${_NAME} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE}) + endif() - # When being installed, we lose the absl_ prefix. We want to put it back - # to have properly named lib files. This is a no-op when we are not being - # installed. + # When being installed, we lose the absl_ prefix. We want to put it back + # to have properly named lib files. This is a no-op when we are not being + # installed. + if(ABSL_ENABLE_INSTALL) set_target_properties(${_NAME} PROPERTIES OUTPUT_NAME "absl_${_NAME}" + SOVERSION "${ABSL_SOVERSION}" ) - else() - # Generating header-only library - add_library(${_NAME} INTERFACE) - target_include_directories(${_NAME} - INTERFACE - "$" - $ - ) - target_link_libraries(${_NAME} - INTERFACE - ${ABSL_CC_LIB_DEPS} - ${ABSL_CC_LIB_LINKOPTS} - ${ABSL_DEFAULT_LINKOPTS} + endif() + else() + # Generating header-only library + add_library(${_NAME} INTERFACE) + target_include_directories(${_NAME} ${ABSL_INTERNAL_INCLUDE_WARNING_GUARD} + INTERFACE + "$" + $ ) - target_compile_definitions(${_NAME} INTERFACE ${ABSL_CC_LIB_DEFINES}) + + if (_build_type STREQUAL "dll") + if(${_in_dll}) + set(ABSL_CC_LIB_DEPS abseil_dll) + endif() + if(${_in_test_dll}) + set(ABSL_CC_LIB_DEPS abseil_test_dll) + endif() endif() - # TODO currently we don't install googletest alongside abseil sources, so - # installed abseil can't be tested. - if(NOT ABSL_CC_LIB_TESTONLY AND ABSL_ENABLE_INSTALL) - install(TARGETS ${_NAME} EXPORT ${PROJECT_NAME}Targets - RUNTIME DESTINATION ${ABSL_INSTALL_BINDIR} - LIBRARY DESTINATION ${ABSL_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${ABSL_INSTALL_LIBDIR} - ) + target_link_libraries(${_NAME} + INTERFACE + ${ABSL_CC_LIB_DEPS} + ${ABSL_CC_LIB_LINKOPTS} + ${ABSL_DEFAULT_LINKOPTS} + ) + target_compile_definitions(${_NAME} INTERFACE ${ABSL_CC_LIB_DEFINES}) + + if(ABSL_PROPAGATE_CXX_STD) + # Abseil libraries require C++17 as the current minimum standard. + # Top-level application CMake projects should ensure a consistent C++ + # standard for all compiled sources by setting CMAKE_CXX_STANDARD. + target_compile_features(${_NAME} INTERFACE ${ABSL_INTERNAL_CXX_STD_FEATURE}) endif() + endif() - add_library(absl::${ABSL_CC_LIB_NAME} ALIAS ${_NAME}) + if(ABSL_ENABLE_INSTALL) + install(TARGETS ${_NAME} EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) endif() + + add_library(absl::${ABSL_CC_LIB_NAME} ALIAS ${_NAME}) endfunction() # absl_cc_test() @@ -205,11 +395,11 @@ endfunction() # "awesome_test.cc" # DEPS # absl::awesome -# gmock -# gtest_main +# GTest::gmock +# GTest::gtest_main # ) function(absl_cc_test) - if(NOT ABSL_RUN_TESTS) + if(NOT (BUILD_TESTING AND ABSL_BUILD_TESTING)) return() endif() @@ -221,35 +411,55 @@ function(absl_cc_test) ) set(_NAME "absl_${ABSL_CC_TEST_NAME}") + add_executable(${_NAME} "") target_sources(${_NAME} PRIVATE ${ABSL_CC_TEST_SRCS}) target_include_directories(${_NAME} PUBLIC ${ABSL_COMMON_INCLUDE_DIRS} - PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} - ) - target_compile_definitions(${_NAME} - PUBLIC ${ABSL_CC_TEST_DEFINES} + PRIVATE ${absl_gtest_src_dir}/googletest/include ${absl_gtest_src_dir}/googlemock/include ) + + if (${ABSL_BUILD_DLL}) + target_compile_definitions(${_NAME} + PUBLIC + ${ABSL_CC_TEST_DEFINES} + ABSL_CONSUME_DLL + ABSL_CONSUME_TEST_DLL + GTEST_LINKED_AS_SHARED_LIBRARY=1 + ) + + # Replace dependencies on targets inside the DLL with abseil_dll itself. + absl_internal_dll_targets( + DEPS ${ABSL_CC_TEST_DEPS} + OUTPUT ABSL_CC_TEST_DEPS + ) + absl_internal_dll_targets( + DEPS ${ABSL_CC_TEST_LINKOPTS} + OUTPUT ABSL_CC_TEST_LINKOPTS + ) + else() + target_compile_definitions(${_NAME} + PUBLIC + ${ABSL_CC_TEST_DEFINES} + ) + endif() target_compile_options(${_NAME} PRIVATE ${ABSL_CC_TEST_COPTS} ) + target_link_libraries(${_NAME} PUBLIC ${ABSL_CC_TEST_DEPS} PRIVATE ${ABSL_CC_TEST_LINKOPTS} ) - # Add all Abseil targets to a a folder in the IDE for organization. + # Add all Abseil targets to a folder in the IDE for organization. set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + if(ABSL_PROPAGATE_CXX_STD) + # Abseil libraries require C++17 as the current minimum standard. + # Top-level application CMake projects should ensure a consistent C++ + # standard for all compiled sources by setting CMAKE_CXX_STANDARD. + target_compile_features(${_NAME} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE}) + endif() add_test(NAME ${_NAME} COMMAND ${_NAME}) endfunction() - - -function(check_target my_target) - if(NOT TARGET ${my_target}) - message(FATAL_ERROR " ABSL: compiling absl requires a ${my_target} CMake target in your project, - see CMake/README.md for more details") - endif(NOT TARGET ${my_target}) -endfunction() diff --git a/CMake/AbseilInstallDirs.cmake b/CMake/AbseilInstallDirs.cmake deleted file mode 100644 index b67272f830c..00000000000 --- a/CMake/AbseilInstallDirs.cmake +++ /dev/null @@ -1,20 +0,0 @@ -include(GNUInstallDirs) - -# absl_VERSION is only set if we are an LTS release being installed, in which -# case it may be into a system directory and so we need to make subdirectories -# for each installed version of Abseil. This mechanism is implemented in -# Abseil's internal Copybara (https://github.com/google/copybara) workflows and -# isn't visible in the CMake buildsystem itself. - -if(absl_VERSION) - set(ABSL_SUBDIR "${PROJECT_NAME}_${PROJECT_VERSION}") - set(ABSL_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}/${ABSL_SUBDIR}") - set(ABSL_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${ABSL_SUBDIR}") - set(ABSL_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/{ABSL_SUBDIR}") - set(ABSL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/${ABSL_SUBDIR}") -else() - set(ABSL_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}") - set(ABSL_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") - set(ABSL_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") - set(ABSL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}") -endif() \ No newline at end of file diff --git a/CMake/Googletest/CMakeLists.txt.in b/CMake/Googletest/CMakeLists.txt.in index d60a33e9ac6..3db48341a25 100644 --- a/CMake/Googletest/CMakeLists.txt.in +++ b/CMake/Googletest/CMakeLists.txt.in @@ -1,15 +1,14 @@ -cmake_minimum_required(VERSION 2.8.2) +cmake_minimum_required(VERSION 3.16) -project(googletest-download NONE) +project(googletest-external NONE) include(ExternalProject) ExternalProject_Add(googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG master - SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" - BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" + URL "${absl_gtest_download_url}" # May be empty + SOURCE_DIR "${absl_gtest_src_dir}" + BINARY_DIR "${absl_gtest_build_dir}" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" TEST_COMMAND "" -) \ No newline at end of file +) diff --git a/CMake/Googletest/DownloadGTest.cmake b/CMake/Googletest/DownloadGTest.cmake index 3c682aef07f..9d071c91701 100644 --- a/CMake/Googletest/DownloadGTest.cmake +++ b/CMake/Googletest/DownloadGTest.cmake @@ -1,32 +1,41 @@ -# Downloads and unpacks googletest at configure time. Based on the instructions -# at https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project +# Integrates googletest at configure time. Based on the instructions at +# https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project -# Download the latest googletest from Github master +# Set up the external googletest project, downloading the latest from Github +# master if requested. configure_file( ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in - ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt + ${CMAKE_BINARY_DIR}/googletest-external/CMakeLists.txt ) -# Configure and build the downloaded googletest source +set(ABSL_SAVE_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) +set(ABSL_SAVE_CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +if (BUILD_SHARED_LIBS) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_CREATE_SHARED_LIBRARY=1") +endif() + +# Configure and build the googletest source. execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-external ) if(result) message(FATAL_ERROR "CMake step for googletest failed: ${result}") endif() execute_process(COMMAND ${CMAKE_COMMAND} --build . RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-external) if(result) message(FATAL_ERROR "Build step for googletest failed: ${result}") endif() +set(CMAKE_CXX_FLAGS ${ABSL_SAVE_CMAKE_CXX_FLAGS}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${ABSL_SAVE_CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + # Prevent overriding the parent project's compiler/linker settings on Windows set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Add googletest directly to our build. This defines the gtest and gtest_main # targets. -add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src - ${CMAKE_BINARY_DIR}/googletest-build - EXCLUDE_FROM_ALL) +add_subdirectory(${absl_gtest_src_dir} ${absl_gtest_build_dir} EXCLUDE_FROM_ALL) diff --git a/CMake/README.md b/CMake/README.md index 04d5df3ab09..ecaeaf067e4 100644 --- a/CMake/README.md +++ b/CMake/README.md @@ -20,8 +20,10 @@ googletest framework ### Step-by-Step Instructions 1. If you want to build the Abseil tests, integrate the Abseil dependency -[Google Test](https://github.com/google/googletest) into your CMake project. To disable Abseil tests, you have to pass -`-DBUILD_TESTING=OFF` when configuring your project with CMake. +[Google Test](https://github.com/google/googletest) into your CMake +project. To disable Abseil tests, you have to pass either +`-DBUILD_TESTING=OFF` or `-DABSL_BUILD_TESTING=OFF` when configuring your +project with CMake. 2. Download Abseil and copy it into a subdirectory in your CMake project or add Abseil as a [git submodule](https://git-scm.com/docs/git-submodule) in your @@ -34,15 +36,16 @@ to include Abseil directly in your CMake project. 4. Add the **absl::** target you wish to use to the [`target_link_libraries()`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) section of your executable or of your library.
-Here is a short CMakeLists.txt example of a project file using Abseil. +Here is a short CMakeLists.txt example of an application project using Abseil. ```cmake -cmake_minimum_required(VERSION 3.5) -project(my_project) +cmake_minimum_required(VERSION 3.16) +project(my_app_project) # Pick the C++ standard to compile with. -# Abseil currently supports C++11, C++14, and C++17. -set(CMAKE_CXX_STANDARD 11) +# Abseil currently supports C++17 and C++20. +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) add_subdirectory(abseil-cpp) @@ -50,9 +53,48 @@ add_executable(my_exe source.cpp) target_link_libraries(my_exe absl::base absl::synchronization absl::strings) ``` +Note that if you are developing a library designed for use by other clients, you +should instead leave `CMAKE_CXX_STANDARD` unset (or only set if being built as +the current top-level CMake project) and configure the minimum required C++ +standard at the target level. If you require a later minimum C++ standard than +Abseil does, it's a good idea to also enforce that `CMAKE_CXX_STANDARD` (which +will control Abseil library targets) is set to at least that minimum. For +example: + +```cmake +cmake_minimum_required(VERSION 3.16) +project(my_lib_project) + +# Leave C++ standard up to the root application, so set it only if this is the +# current top-level CMake project. +if(CMAKE_SOURCE_DIR STREQUAL my_lib_project_SOURCE_DIR) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() + +add_subdirectory(abseil-cpp) + +add_library(my_lib source.cpp) +target_link_libraries(my_lib absl::base absl::synchronization absl::strings) + +# Enforce that my_lib requires C++17. Important to document for clients that they +# must set CMAKE_CXX_STANDARD to 17 or higher for proper Abseil ABI compatibility +# (since otherwise, Abseil library targets could be compiled with a lower C++ +# standard than my_lib). +target_compile_features(my_lib PUBLIC cxx_std_17) +if(CMAKE_CXX_STANDARD LESS 17) + message(FATAL_ERROR + "my_lib_project requires CMAKE_CXX_STANDARD >= 17 (got: ${CMAKE_CXX_STANDARD})") +endif() +``` + +Then the top-level application project that uses your library is responsible for +setting a consistent `CMAKE_CXX_STANDARD` that is sufficiently high. + ### Running Abseil Tests with CMake -Use the `-DABSL_RUN_TESTS=ON` flag to run Abseil tests. Note that if the `-DBUILD_TESTING=OFF` flag is passed then Abseil tests will not be run. +Use the `-DABSL_BUILD_TESTING=ON` flag to run Abseil tests. Note that +BUILD_TESTING must also be on (the default). You will need to provide Abseil with a Googletest dependency. There are two options for how to do this: @@ -70,7 +112,7 @@ For example, to run just the Abseil tests, you could use this script: cd path/to/abseil-cpp mkdir build cd build -cmake -DABSL_USE_GOOGLETEST_HEAD=ON -DABSL_RUN_TESTS=ON .. +cmake -DABSL_BUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=ON .. make -j ctest ``` @@ -93,9 +135,54 @@ absl::flags absl::memory absl::meta absl::numeric -absl::random +absl::random_random absl::strings absl::synchronization absl::time absl::utility ``` + +## Traditional CMake Set-Up + +For larger projects, it may make sense to use the traditional CMake set-up where you build and install projects separately. + +First, you'd need to build and install Google Test: +``` +cmake -S /source/googletest -B /build/googletest -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/installation/dir -DBUILD_GMOCK=ON +cmake --build /build/googletest --target install +``` + +Then you need to configure and build Abseil. Make sure you enable `ABSL_USE_EXTERNAL_GOOGLETEST` and `ABSL_FIND_GOOGLETEST`. You also need to enable `ABSL_ENABLE_INSTALL` so that you can install Abseil itself. +``` +cmake -S /source/abseil-cpp -B /build/abseil-cpp -DCMAKE_PREFIX_PATH=/installation/dir -DCMAKE_INSTALL_PREFIX=/installation/dir -DABSL_ENABLE_INSTALL=ON -DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON +cmake --build /temporary/build/abseil-cpp +``` + +(`CMAKE_PREFIX_PATH` is where you already have Google Test installed; `CMAKE_INSTALL_PREFIX` is where you want to have Abseil installed; they can be different.) + +Run the tests: +``` +ctest --test-dir /temporary/build/abseil-cpp +``` + +And finally install: +``` +cmake --build /temporary/build/abseil-cpp --target install +``` + +# CMake Option Synopsis + +## Enable Standard CMake Installation + +`-DABSL_ENABLE_INSTALL=ON` + +## Google Test Options + +`-DABSL_BUILD_TESTING=ON` must be set to enable testing + +- Have Abseil download and build Google Test for you: `-DABSL_USE_EXTERNAL_GOOGLETEST=OFF` (default) + - Download and build latest Google Test: `-DABSL_USE_GOOGLETEST_HEAD=ON` + - Download specific Google Test version (ZIP archive): `-DABSL_GOOGLETEST_DOWNLOAD_URL=https://.../version.zip` + - Use Google Test from specific local directory: `-DABSL_LOCAL_GOOGLETEST_DIR=/path/to/googletest` +- Use Google Test included elsewhere in your project: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON` +- Use standard CMake `find_package(CTest)` to find installed Google Test: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON` diff --git a/CMake/abslConfig.cmake.in b/CMake/abslConfig.cmake.in index 60847fa7723..62d246d01e0 100644 --- a/CMake/abslConfig.cmake.in +++ b/CMake/abslConfig.cmake.in @@ -1,7 +1,8 @@ # absl CMake configuration file. -include(FindThreads) +include(CMakeFindDependencyMacro) +find_dependency(Threads) @PACKAGE_INIT@ -include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") \ No newline at end of file +include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") diff --git a/CMake/install_test_project/CMakeLists.txt b/CMake/install_test_project/CMakeLists.txt index 06b797e9edd..3229887c8d6 100644 --- a/CMake/install_test_project/CMakeLists.txt +++ b/CMake/install_test_project/CMakeLists.txt @@ -15,13 +15,11 @@ # A simple CMakeLists.txt for testing cmake installation -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(absl_cmake_testing CXX) -set(CMAKE_CXX_STANDARD 11) - add_executable(simple simple.cc) find_package(absl REQUIRED) -target_link_libraries(simple absl::strings) +target_link_libraries(simple absl::strings absl::config) diff --git a/CMake/install_test_project/simple.cc b/CMake/install_test_project/simple.cc index e9e352912ba..7daa7f09017 100644 --- a/CMake/install_test_project/simple.cc +++ b/CMake/install_test_project/simple.cc @@ -14,8 +14,17 @@ // limitations under the License. #include +#include "absl/base/config.h" #include "absl/strings/substitute.h" +#if !defined(ABSL_LTS_RELEASE_VERSION) || ABSL_LTS_RELEASE_VERSION != 99998877 +#error ABSL_LTS_RELEASE_VERSION is not set correctly. +#endif + +#if !defined(ABSL_LTS_RELEASE_PATCH_LEVEL) || ABSL_LTS_RELEASE_PATCH_LEVEL != 0 +#error ABSL_LTS_RELEASE_PATCH_LEVEL is not set correctly. +#endif + int main(int argc, char** argv) { for (int i = 0; i < argc; ++i) { std::cout << absl::Substitute("Arg $0: $1\n", i, argv[i]); diff --git a/CMake/install_test_project/test.sh b/CMake/install_test_project/test.sh index 99989b031d3..962bc8d2d83 100755 --- a/CMake/install_test_project/test.sh +++ b/CMake/install_test_project/test.sh @@ -13,70 +13,61 @@ # 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. - -# "Unit" and integration tests for Absl CMake installation - -# TODO(absl-team): This script isn't fully hermetic because -# -DABSL_USE_GOOGLETEST_HEAD=ON means that this script isn't pinned to a fixed -# version of GoogleTest. This means that an upstream change to GoogleTest could -# break this test. Fix this by allowing this script to pin to a known-good -# version of GoogleTest. +# +# Unit and integration tests for Abseil LTS CMake installation # Fail on any error. Treat unset variables an error. Print commands as executed. set -euox pipefail -install_absl() { - pushd "${absl_build_dir}" - if [[ "${#}" -eq 1 ]]; then - cmake -DCMAKE_INSTALL_PREFIX="${1}" "${absl_dir}" - else - cmake "${absl_dir}" - fi - cmake --build . --target install -- -j - popd -} - -uninstall_absl() { - xargs rm < "${absl_build_dir}"/install_manifest.txt - rm -rf "${absl_build_dir}" - mkdir -p "${absl_build_dir}" -} - -lts_install="" - -while getopts ":l" lts; do - case "${lts}" in - l ) - lts_install="true" - ;; - esac -done - absl_dir=/abseil-cpp -absl_build_dir=/buildfs/absl-build -project_dir="${absl_dir}"/CMake/install_test_project +absl_build_dir=/buildfs +googletest_builddir=/googletest_builddir +googletest_archive="googletest-${ABSL_GOOGLETEST_VERSION}.tar.gz" +project_dir="${absl_dir}/CMake/install_test_project" project_build_dir=/buildfs/project-build -mkdir -p "${absl_build_dir}" -mkdir -p "${project_build_dir}" - -if [[ "${lts_install}" ]]; then - install_dir="/usr/local" -else - install_dir="${project_build_dir}"/install +build_shared_libs="OFF" +if [ "${LINK_TYPE:-}" = "DYNAMIC" ]; then + build_shared_libs="ON" fi -mkdir -p "${install_dir}" -# Test build, install, and link against installed abseil -pushd "${project_build_dir}" -if [[ "${lts_install}" ]]; then - install_absl - cmake "${project_dir}" -else - install_absl "${install_dir}" - cmake "${project_dir}" -DCMAKE_PREFIX_PATH="${install_dir}" -fi +# Build and install GoogleTest +mkdir "${googletest_builddir}" +pushd "${googletest_builddir}" +curl -L "${ABSL_GOOGLETEST_DOWNLOAD_URL}" --output "${googletest_archive}" +tar -xz -f "${googletest_archive}" +pushd "googletest-${ABSL_GOOGLETEST_VERSION}" +mkdir build +pushd build +cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS="${build_shared_libs}" .. +make -j $(nproc) +make install +ldconfig +popd +popd +popd +# Run the LTS transformations +./create_lts.py 99998877 + +# Build and install Abseil +pushd "${absl_build_dir}" +cmake "${absl_dir}" \ + -DABSL_USE_EXTERNAL_GOOGLETEST=ON \ + -DABSL_FIND_GOOGLETEST=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DABSL_BUILD_TESTING=ON \ + -DBUILD_SHARED_LIBS="${build_shared_libs}" +make -j $(nproc) +ctest -j $(nproc) --output-on-failure +make install +ldconfig +popd + +# Test the project against the installed Abseil +mkdir -p "${project_build_dir}" +pushd "${project_build_dir}" +cmake "${project_dir}" cmake --build . --target simple output="$(${project_build_dir}/simple "printme" 2>&1)" @@ -88,57 +79,35 @@ fi popd -# Test that we haven't accidentally made absl::abslblah -pushd "${install_dir}" - -# Starting in CMake 3.12 the default install dir is lib$bit_width -if [[ -d lib64 ]]; then - libdir="lib64" -elif [[ -d lib ]]; then - libdir="lib" -else - echo "ls *, */*, */*/*:" - ls * - ls */* - ls */*/* - echo "unknown lib dir" -fi - -if [[ "${lts_install}" ]]; then - # LTS versions append the date of the release to the subdir. - # 9999/99/99 is the dummy date used in the local_lts workflow. - absl_subdir="absl_99999999" -else - absl_subdir="absl" -fi - -if ! grep absl::strings "${libdir}/cmake/${absl_subdir}/abslTargets.cmake"; then - cat "${libdir}"/cmake/absl/abslTargets.cmake +if ! grep absl::strings "/usr/local/lib/cmake/absl/abslTargets.cmake"; then + cat "/usr/local/lib/cmake/absl/abslTargets.cmake" echo "CMake targets named incorrectly" exit 1 fi -uninstall_absl -popd +pushd "${HOME}" +cat > hello-abseil.cc << EOF +#include -if [[ ! "${lts_install}" ]]; then - # Test that we warn if installed without a prefix or a system prefix - output="$(install_absl 2>&1)" - if [[ "${output}" != *"Please set CMAKE_INSTALL_PREFIX"* ]]; then - echo "Install without prefix didn't warn as expected. Output:" - echo "${output}" - exit 1 - fi - uninstall_absl - - output="$(install_absl /usr 2>&1)" - if [[ "${output}" != *"Please set CMAKE_INSTALL_PREFIX"* ]]; then - echo "Install with /usr didn't warn as expected. Output:" - echo "${output}" - exit 1 - fi - uninstall_absl +#include "absl/strings/str_format.h" + +int main(int argc, char **argv) { + absl::PrintF("Hello Abseil!\n"); + return EXIT_SUCCESS; +} +EOF + +if [ "${LINK_TYPE:-}" != "DYNAMIC" ]; then + pc_args=($(pkg-config --cflags --libs --static absl_str_format)) + g++ -static -o hello-abseil hello-abseil.cc "${pc_args[@]}" +else + pc_args=($(pkg-config --cflags --libs absl_str_format)) + g++ -o hello-abseil hello-abseil.cc "${pc_args[@]}" fi +hello="$(./hello-abseil)" +[[ "${hello}" == "Hello Abseil!" ]] + +popd echo "Install test complete!" exit 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 86f56344e74..3583e3dbe46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,39 +14,62 @@ # limitations under the License. # -# Most widely used distributions have cmake 3.5 or greater available as of March -# 2019. A notable exception is RHEL-7 (CentOS7). You can install a current -# version of CMake by first installing Extra Packages for Enterprise Linux -# (https://fedoraproject.org/wiki/EPEL#Extra_Packages_for_Enterprise_Linux_.28EPEL.29) -# and then issuing `yum install cmake3` on the command line. -cmake_minimum_required(VERSION 3.5) +# https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md +# As of 2024-07-01, CMake 3.16 is the minimum supported version. +cmake_minimum_required(VERSION 3.16) -# Compiler id for Apple Clang is now AppleClang. -cmake_policy(SET CMP0025 NEW) +# Allow the user to specify the CMAKE_MSVC_DEBUG_INFORMATION_FORMAT +if (POLICY CMP0141) + cmake_policy(SET CMP0141 NEW) +endif (POLICY CMP0141) -# if command can use IN_LIST -cmake_policy(SET CMP0057 NEW) +project(absl LANGUAGES CXX) +set(ABSL_SOVERSION 0) +include(CTest) -# Project version variables are the empty std::string if version is unspecified -cmake_policy(SET CMP0048 NEW) - -project(absl CXX) +# Output directory is correct by default for most build setups. However, when +# building Abseil as a DLL, it is important to have the DLL in the same +# directory as the executable using it. Thus, we put all executables in a single +# /bin directory. +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) # when absl is included as subproject (i.e. using add_subdirectory(abseil-cpp)) # in the source tree of a project that uses it, install rules are disabled. -if(NOT "^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$") - set(ABSL_ENABLE_INSTALL FALSE) +if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + option(ABSL_ENABLE_INSTALL "Enable install rule" OFF) else() - set(ABSL_ENABLE_INSTALL TRUE) + option(ABSL_ENABLE_INSTALL "Enable install rule" ON) endif() +set(CMAKE_INSTALL_RPATH "$ORIGIN") +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON) +set(CMAKE_BUILD_RPATH_USE_ORIGIN ON) + +option(ABSL_PROPAGATE_CXX_STD + "Use CMake C++ standard meta features (e.g. cxx_std_17) that propagate to targets that link to Abseil" + ON) + +option(ABSL_USE_SYSTEM_INCLUDES + "Silence warnings in Abseil headers by marking them as SYSTEM includes" + OFF) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake ${CMAKE_CURRENT_LIST_DIR}/absl/copts ) -include(AbseilInstallDirs) +option(ABSL_MSVC_STATIC_RUNTIME + "Link static runtime libraries" + OFF) +if(ABSL_MSVC_STATIC_RUNTIME) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +else() + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") +endif() + include(CMakePackageConfigHelpers) +include(GNUInstallDirs) +include(AbseilDll) include(AbseilHelpers) @@ -74,37 +97,74 @@ endif() ## pthread find_package(Threads REQUIRED) -option(ABSL_USE_GOOGLETEST_HEAD - "If ON, abseil will download HEAD from googletest at config time." OFF) +include(CMakeDependentOption) -option(ABSL_RUN_TESTS "If ON, Abseil tests will be run." OFF) +option(ABSL_BUILD_TESTING + "If ON, Abseil will build all of Abseil's own tests." OFF) -if(${ABSL_RUN_TESTS}) - # enable CTest. This will set BUILD_TESTING to ON unless otherwise specified - # on the command line - include(CTest) - enable_testing() -endif() +option(ABSL_BUILD_TEST_HELPERS + "If ON, Abseil will build libraries that you can use to write tests against Abseil code. This option requires that Abseil is configured to use GoogleTest." + OFF) -## check targets -if(BUILD_TESTING) +option(ABSL_USE_EXTERNAL_GOOGLETEST + "If ON, Abseil will assume that the targets for GoogleTest are already provided by the including project. This makes sense when Abseil is used with add_subdirectory." OFF) - if(${ABSL_USE_GOOGLETEST_HEAD}) - include(CMake/Googletest/DownloadGTest.cmake) - set(absl_gtest_src_dir ${CMAKE_BINARY_DIR}/googletest-src) - set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build) - endif() +cmake_dependent_option(ABSL_FIND_GOOGLETEST + "If ON, Abseil will use find_package(GTest) rather than assuming that GoogleTest is already provided by the including project." + ON + "ABSL_USE_EXTERNAL_GOOGLETEST" + OFF) - check_target(gtest) - check_target(gtest_main) - check_target(gmock) - list(APPEND ABSL_TEST_COMMON_LIBRARIES - gtest_main - gtest - gmock - ${CMAKE_THREAD_LIBS_INIT} +option(ABSL_USE_GOOGLETEST_HEAD + "If ON, abseil will download HEAD from GoogleTest at config time." OFF) + +set(ABSL_GOOGLETEST_DOWNLOAD_URL "" CACHE STRING "If set, download GoogleTest from this URL") + +set(ABSL_LOCAL_GOOGLETEST_DIR "/usr/src/googletest" CACHE PATH + "If ABSL_USE_GOOGLETEST_HEAD is OFF and ABSL_GOOGLETEST_URL is not set, specifies the directory of a local GoogleTest checkout." ) + +option(ABSL_BUILD_MONOLITHIC_SHARED_LIBS + "Build Abseil as a single shared library (always enabled for Windows)" + OFF +) +if(NOT BUILD_SHARED_LIBS AND ABSL_BUILD_MONOLITHIC_SHARED_LIBS) + message(WARNING "Not building a shared library because BUILD_SHARED_LIBS is not set. Ignoring ABSL_BUILD_MONOLITHIC_SHARED_LIBS.") +endif() + +if((BUILD_TESTING AND ABSL_BUILD_TESTING) OR ABSL_BUILD_TEST_HELPERS) + if (ABSL_USE_EXTERNAL_GOOGLETEST) + if (ABSL_FIND_GOOGLETEST) + find_package(GTest REQUIRED) + elseif(NOT TARGET GTest::gtest) + if(TARGET gtest) + # When Google Test is included directly rather than through find_package, the aliases are missing. + add_library(GTest::gtest ALIAS gtest) + add_library(GTest::gtest_main ALIAS gtest_main) + add_library(GTest::gmock ALIAS gmock) + add_library(GTest::gmock_main ALIAS gmock_main) + else() + message(FATAL_ERROR "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.") + endif() + endif() + else() + set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build) + if(ABSL_USE_GOOGLETEST_HEAD AND ABSL_GOOGLETEST_DOWNLOAD_URL) + message(FATAL_ERROR "Do not set both ABSL_USE_GOOGLETEST_HEAD and ABSL_GOOGLETEST_DOWNLOAD_URL") + endif() + if(ABSL_USE_GOOGLETEST_HEAD) + set(absl_gtest_download_url "https://github.com/google/googletest/archive/main.zip") + elseif(ABSL_GOOGLETEST_DOWNLOAD_URL) + set(absl_gtest_download_url ${ABSL_GOOGLETEST_DOWNLOAD_URL}) + endif() + if(absl_gtest_download_url) + set(absl_gtest_src_dir ${CMAKE_BINARY_DIR}/googletest-src) + else() + set(absl_gtest_src_dir ${ABSL_LOCAL_GOOGLETEST_DIR}) + endif() + include(CMake/Googletest/DownloadGTest.cmake) + endif() endif() add_subdirectory(absl) @@ -125,16 +185,16 @@ if(ABSL_ENABLE_INSTALL) # install as a subdirectory only install(EXPORT ${PROJECT_NAME}Targets NAMESPACE absl:: - DESTINATION "${ABSL_INSTALL_CONFIGDIR}" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) configure_package_config_file( CMake/abslConfig.cmake.in "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - INSTALL_DESTINATION "${ABSL_INSTALL_CONFIGDIR}" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - DESTINATION "${ABSL_INSTALL_CONFIGDIR}" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) # Abseil only has a version in LTS releases. This mechanism is accomplished @@ -147,14 +207,74 @@ if(ABSL_ENABLE_INSTALL) ) install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" - DESTINATION ${ABSL_INSTALL_CONFIGDIR} + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) endif() # absl_VERSION + # Install the headers except for "options.h" which is installed separately. install(DIRECTORY absl - DESTINATION ${ABSL_INSTALL_INCLUDEDIR} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.inc" PATTERN "*.h" - ) + PATTERN "options.h" EXCLUDE + PATTERN "copts" EXCLUDE + PATTERN "testdata" EXCLUDE + ) + + # Rewrite options.h to use the compiled ABI. + file(READ "absl/base/options.h" ABSL_INTERNAL_OPTIONS_H_CONTENTS) + + # Handle features that require at least C++20. + if (ABSL_INTERNAL_AT_LEAST_CXX20) + foreach(FEATURE "ORDERING" "SOURCE_LOCATION") + string(REPLACE + "#define ABSL_OPTION_USE_STD_${FEATURE} 2" + "#define ABSL_OPTION_USE_STD_${FEATURE} 1" + ABSL_INTERNAL_OPTIONS_H_PINNED + "${ABSL_INTERNAL_OPTIONS_H_CONTENTS}") + set(ABSL_INTERNAL_OPTIONS_H_CONTENTS "${ABSL_INTERNAL_OPTIONS_H_PINNED}") + endforeach() + endif() + + # Handle features that require at least C++17. + if (ABSL_INTERNAL_AT_LEAST_CXX17) + foreach(FEATURE "ANY" "OPTIONAL" "STRING_VIEW" "VARIANT") + string(REPLACE + "#define ABSL_OPTION_USE_STD_${FEATURE} 2" + "#define ABSL_OPTION_USE_STD_${FEATURE} 1" + ABSL_INTERNAL_OPTIONS_H_PINNED + "${ABSL_INTERNAL_OPTIONS_H_CONTENTS}") + set(ABSL_INTERNAL_OPTIONS_H_CONTENTS "${ABSL_INTERNAL_OPTIONS_H_PINNED}") + endforeach() + endif() + + # Any feature that still has the value of 2 (because it was not handled above) + # should be set to 0. + string(REGEX REPLACE + "#define ABSL_OPTION_USE_STD_([^ ]*) 2" + "#define ABSL_OPTION_USE_STD_\\1 0" + ABSL_INTERNAL_OPTIONS_H_PINNED + "${ABSL_INTERNAL_OPTIONS_H_CONTENTS}") + + # If the file already exists, check if it matches the new contents. + # This avoids writing the file if it is already up-to-date when the CMake + # generation is triggered and triggering unnecessary rebuilds. + set(ABSL_INTERNAL_OPTIONS_H_PINNED_NEEDS_UPDATE TRUE) + if (EXISTS "${CMAKE_BINARY_DIR}/options-pinned.h") + file(READ "${CMAKE_BINARY_DIR}/options-pinned.h" ABSL_INTERNAL_OPTIONS_PINNED_H_CONTENTS) + if ("${ABSL_INTERNAL_OPTIONS_H_PINNED}" STREQUAL "${ABSL_INTERNAL_OPTIONS_PINNED_H_CONTENTS}") + set(ABSL_INTERNAL_OPTIONS_H_PINNED_NEEDS_UPDATE FALSE) + endif() + endif() + + # If the file needs an update, generate it. + if (ABSL_INTERNAL_OPTIONS_H_PINNED_NEEDS_UPDATE) + file(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/options-pinned.h" CONTENT "${ABSL_INTERNAL_OPTIONS_H_PINNED}") + endif() + + install(FILES "${CMAKE_BINARY_DIR}/options-pinned.h" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/absl/base + RENAME "options.h") + endif() # ABSL_ENABLE_INSTALL diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f4cb4a29ed0..a87254c5035 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -75,9 +75,9 @@ will be expected to conform to the style outlined ## Guidelines for Pull Requests -* If you are a Googler, it is preferable to first create an internal CL and - have it reviewed and submitted. The code propagation process will deliver - the change to GitHub. +* If you are a Googler, it is required that you send us a Piper CL instead of + using the GitHub pull-request process. The code propagation process will + deliver the change to GitHub. * Create **small PRs** that are narrowly focused on **addressing a single concern**. We often receive PRs that are trying to fix several things at a @@ -123,10 +123,13 @@ will be expected to conform to the style outlined ## Running Tests -Use "bazel test <>" functionality to run the unit tests. +If you have [Bazel](https://bazel.build/) installed, use `bazel test +--test_tag_filters="-benchmark" ...` to run the unit tests. -Prerequisites for building and running tests are listed in -[README.md](README.md) +If you are running the Linux operating system and have +[Docker](https://www.docker.com/) installed, you can also run the `linux_*.sh` +scripts under the `ci/`(https://github.com/abseil/abseil-cpp/tree/master/ci) +directory to test Abseil under a variety of conditions. ## Abseil Committers diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 00000000000..30291fe4f18 --- /dev/null +++ b/FAQ.md @@ -0,0 +1,218 @@ +# Abseil FAQ + +## Is Abseil the right home for my utility library? + +Most often the answer to the question is "no." As both the +[About Abseil](https://abseil.io/about/) page and our +[contributing guidelines](https://github.com/abseil/abseil-cpp/blob/master/CONTRIBUTING.md#contribution-guidelines) +explain, Abseil contains a variety of core C++ library code that is widely used +at [Google](https://www.google.com/). As such, Abseil's primary purpose is to be +used as a dependency by Google's open source C++ projects. While we do hope that +Abseil is also useful to the C++ community at large, this added constraint also +means that we are unlikely to accept a contribution of utility code that isn't +already widely used by Google. + +## How do I set the C++ dialect used to build Abseil? + +The short answer is that whatever mechanism you choose, you need to make sure +that you set this option consistently at the global level for your entire +project. If, for example, you want to set the C++ dialect to C++17, with +[Bazel](https://bazel.build/) as the build system and `gcc` or `clang` as the +compiler, there are several ways to do this: * Pass `--cxxopt=-std=c++17` on the +command line (for example, `bazel build --cxxopt=-std=c++17 ...`) * Set the +environment variable `BAZEL_CXXOPTS` (for example, `BAZEL_CXXOPTS=-std=c++17`) * +Add `build --cxxopt=-std=c++17` to your +[`.bazelrc` file](https://docs.bazel.build/versions/master/guide.html#bazelrc) + +If you are using CMake as the build system, you'll need to add a line like +`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. If you are +developing a library designed to be used by other clients, you should instead +leave `CMAKE_CXX_STANDARD` unset and configure the minimum C++ standard required +by each of your library targets via `target_compile_features`. See the +[CMake build instructions](https://github.com/abseil/abseil-cpp/blob/master/CMake/README.md) +for more information. + +For a longer answer to this question and to understand why some other approaches +don't work, see the answer to ["What is ABI and why don't you recommend using a +pre-compiled version of +Abseil?"](#what-is-abi-and-why-dont-you-recommend-using-a-pre-compiled-version-of-abseil) + +## What is ABI and why don't you recommend using a pre-compiled version of Abseil? + +For the purposes of this discussion, +[ABI](https://en.wikipedia.org/wiki/Application_binary_interface) refers to the +compiled representation of code interfaces. This contrasts with +[API](https://en.wikipedia.org/wiki/Application_programming_interface), which +refers to the interfaces defined in the source code. [Abseil has a strong +promise of API compatibility, but does not make any promise of ABI +compatibility](https://abseil.io/about/compatibility). Let's take a look at what +this means in practice. + +You might be tempted to do something like this in a +[Bazel](https://bazel.build/) `BUILD` file: + +``` +# DON'T DO THIS!!! +cc_library( + name = "my_library", + srcs = ["my_library.cc"], + copts = ["-std=c++17"], # May create a mixed-mode compile! + deps = ["@com_google_absl//absl/strings"], +) +``` + +Applying `-std=c++17` to an individual target in your `BUILD` file is going to +compile that specific target in C++17 mode, but it isn't going to ensure the +Abseil library is built in C++17 mode, since the Abseil library itself is a +different build target. If your code includes an Abseil header, then your +program may contain conflicting definitions of the same +class/function/variable/enum, etc. As a rule, all compile options that affect +the ABI of a program need to be applied to the entire build on a global basis. + +C++ has something called the +[One Definition Rule](https://en.wikipedia.org/wiki/One_Definition_Rule) (ODR). +C++ doesn't allow multiple definitions of the same class/function/variable/enum, +etc. ODR violations sometimes result in linker errors, but linkers do not always +catch violations. Uncaught ODR violations can result in strange runtime +behaviors or crashes that can be hard to debug. + +If you build the Abseil library and your code using different compile options +that affect ABI, there is a good chance you will run afoul of the One Definition +Rule. Examples of GCC compile options that affect ABI include (but aren't +limited to) language dialect (e.g. `-std=`), optimization level (e.g. `-O2`), +code generation flags (e.g. `-fexceptions`), and preprocessor defines (e.g. +`-DNDEBUG`). + +If you use a pre-compiled version of Abseil, (for example, from your Linux +distribution package manager or from something like +[vcpkg](https://github.com/microsoft/vcpkg)) you have to be very careful to +ensure ABI compatibility across the components of your program. The only way you +can be sure your program is going to be correct regarding ABI is to ensure +you've used the exact same compile options as were used to build the +pre-compiled library. This does not mean that Abseil cannot work as part of a +Linux distribution since a knowledgeable binary packager will have ensured that +all packages have been built with consistent compile options. This is one of the +reasons we warn against - though do not outright reject - using Abseil as a +pre-compiled library. + +Another possible way that you might run afoul of ABI issues is if you +accidentally include two versions of Abseil in your program. Multiple versions +of Abseil can end up within the same binary if your program uses the Abseil +library and another library also transitively depends on Abseil (resulting in +what is sometimes called the diamond dependency problem). In cases such as this +you must structure your build so that all libraries use the same version of +Abseil. +[Abseil's strong promise of API compatibility between releases](https://abseil.io/about/compatibility) +means the latest "HEAD" release of Abseil is almost certainly the right choice +if you are doing as we recommend and building all of your code from source. + +For these reasons we recommend you avoid pre-compiled code and build the Abseil +library yourself in a consistent manner with the rest of your code. + +## What is "live at head" and how do I do it? + +From Abseil's point-of-view, "live at head" means that every Abseil source +release (which happens on an almost daily basis) is either API compatible with +the previous release, or comes with an automated tool that you can run over code +to make it compatible. In practice, the need to use an automated tool is +extremely rare. This means that upgrading from one source release to another +should be a routine practice that can and should be performed often. + +We recommend you update to the +[latest commit in the `master` branch of Abseil](https://github.com/abseil/abseil-cpp/commits/master) +as often as possible. Not only will you pick up bug fixes more quickly, but if +you have good automated testing, you will catch and be able to fix any +[Hyrum's Law](https://www.hyrumslaw.com/) dependency problems on an incremental +basis instead of being overwhelmed by them and having difficulty isolating them +if you wait longer between updates. + +If you are using the [Bazel](https://bazel.build/) build system with +[Bzlmod](https://bazel.build/external/overview#bzlmod), you can use a +`git_override` in your `MODULE.bazel` file to track the latest commit. + +For example, to update to the latest commit, you would add (or update) the +following snippet in your `MODULE.bazel` file: + +```starlark +bazel_dep(name = "abseil-cpp", version = "20260107.1") + +git_override( + module_name = "abseil-cpp", + remote = "https://github.com/abseil/abseil-cpp.git", + # Replace the following line with the latest commit. + commit = "6ec9964c325db0610a376b3cb81de073ea6ada90", +) +``` + +You can commit the updated `MODULE.bazel` file to your source control every time +you update, and if you have good automated testing, you might even consider +automating this. + +## Why do I see strange behaviors when I use Abseil hash tables? + +Abseil's hash function uses a random seed. + +Many programmers believe incorrectly that this is a defense against +[hash flooding](https://en.wikipedia.org/wiki/Collision_attack#Hash_flooding). +While it does make a hash flooding attack more difficult, Abseil's hash function +prioritizes speed over thorough mixing (and thus is not cryptographically +secure), and the current seed implementation is also not cryptographically +secure. If you are storing a large amount of attacker-controlled data, the most +reliable defense against hash-flooding is to use a container that does not have +`O(n)` worst-case behavior. + +The real reason for hash randomization is to prevent +[Hyrum's Law](https://www.hyrumslaw.com/) dependencies on iteration order. This +has allowed us to roll out steady improvements to the implementation without +breaking users who may have otherwise written code that was dependent on +ordering or other characteristics. + +The current implementation uses a global seed, which, if linked incorrectly +(e.g., static Abseil in multiple DSOs), can cause the ODR violations. If more +than one seed is linked, different calls to the hash function may return +different values, rendering hash elements inaccessible, causing crashes, or +other arbitrarily bad behaviors. + +We are often asked for a knob to disable hash randomization. The answer is a +hard "no", even under test or under a flag, because people will find a way to +force it and allow their code or tests to depend on it. At Google-scale, the +compute costs that are saved by preserving the ability to improve the +implementation far outweigh the inconvenience of learning how to write code +resilient to change. + +## How do I use the LLVM Sanitizers with Abseil? + +LLVM Sanitizers are a suite of powerful, dynamic analysis tools that +automatically detect various critical bugs during program execution. They work +by instrumenting the compiled binary and linking a runtime library to intercept +operations and report issues. + +We receive many incorrect bug reports from users trying to use the sanitizers. +The most common cause of these issues is ODR violations in the form of an +instrumentation mismatch. This happens when users try to link libraries compiled +with sanitizer instrumentation with uninstrumented libraries. It is important +that all code in the application is built with the same sanitizer configuration. + +The easiest way to do this is to use [Bazel](https://bazel.build) and pass the +sanitizer options on the commandline, but it is important not to overlook the +importance of avoiding precompiled system libraries, including the C++ standard +library. For instance, +[MemorySanitizer requires an instrumented `libc++`](https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo). + +Since most users are not going to build `libc++` with Bazel, here is a +MemorySanitizer recipe that currently works (and could easily be tweaked for +ThreadSanitizer and friends): + +```shell +# From the root of the LLVM source tree, configure libc++ to be instrumented with MSAN: +cmake -G Ninja -S runtimes -B build_msan -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;libunwind" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="${HOME}/llvm-msan" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_USE_SANITIZER=MemoryWithOrigins -DLLVM_TARGETS_TO_BUILD="host" + +# Build and install it into ${HOME}/llvm-msan +ninja -C build_msan install-cxx install-cxxabi install-unwind + +# Then build (or test) your code like this: +bazel test --repo_env=CC=clang --repo_env=BAZEL_CXXOPTS=nostdinc++ --repo_env=BAZEL_LINKOPTS=-L${HOME}/llvm-msan/lib:-lc++:-lc++abi:-lgcc_s:-lm:-Wl,-rpath=${HOME}/llvm-msan/lib --repo_env=CPLUS_INCLUDE_PATH=${HOME}/llvm-msan/include/c++/v1 --copt=-fsanitize=memory --linkopt=-fsanitize=memory --linkopt=-fsanitize-link-c++-runtime ... +``` + +You should consider adding these options to a +[`.bazelrc`](https://bazel.build/run/bazelrc) file to avoid retyping them. diff --git a/LTS.md b/LTS.md deleted file mode 100644 index 94363b6a36f..00000000000 --- a/LTS.md +++ /dev/null @@ -1,15 +0,0 @@ -# Long Term Support (LTS) Branches - -This repository contains periodic snapshots of the Abseil codebase that are -Long Term Support (LTS) branches. An LTS branch allows you to use a known -version of Abseil without interfering with other projects which may also, in -turn, use Abseil. (For more information about our releases, see the -[Abseil Release Management](https://abseil.io/about/releases) guide.) - -## LTS Branches - -The following lists LTS branches and the dates on which they have been released: - -* [LTS Branch December 18, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_12_18/) -* [LTS Branch June 20, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_06_20/) -* [LTS Branch August 8, 2019](https://github.com/abseil/abseil-cpp/tree/lts_2019_08_08/) diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 00000000000..00ab4c35dda --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,52 @@ +# Copyright 2024 The Abseil Authors. +# +# 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 +# +# https://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. + +# https://bazel.build/external/overview#bzlmod + +module( + name = "abseil-cpp", + version = "head", +) + +cc_configure = use_extension( + "@rules_cc//cc:extensions.bzl", + "cc_configure_extension", + dev_dependency = True, +) +use_repo(cc_configure, "local_config_cc") + +bazel_dep(name = "rules_cc", version = "0.2.22") +bazel_dep(name = "bazel_skylib", version = "1.9.2") +bazel_dep(name = "platforms", version = "1.1.0") + +bazel_dep( + name = "google_benchmark", + version = "1.9.5", + dev_dependency = True, +) + +# Note: Googletest is NOT a dev_dependency. Some Abseil test utilities +# intended to be used by Abseil users depend on GoogleTest. +bazel_dep( + name = "googletest", + version = "1.18.0", +) + +# Note: Gloop is NOT a dev_dependency, but should never be used directly. It is only included here +# so that we can give visibility into Abseil internals. +bazel_dep( + name = "gloop", + version = "20260708.rc1", + repo_name = "do_not_use_for_gloop_visibility_only", +) diff --git a/PrivacyInfo.xcprivacy b/PrivacyInfo.xcprivacy new file mode 100644 index 00000000000..3ff4a9d98b1 --- /dev/null +++ b/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyTracking + + NSPrivacyCollectedDataTypes + + NSPrivacyTrackingDomains + + NSPrivacyAccessedAPITypes + + + diff --git a/README.md b/README.md index 85de569658c..c2c851ec35b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Abseil - C++ Common Libraries The repository contains the Abseil C++ library code. Abseil is an open-source -collection of C++ code (compliant to C++11) designed to augment the C++ +collection of C++ code (compliant to C++17) designed to augment the C++ standard library. ## Table of Contents @@ -9,7 +9,9 @@ standard library. - [About Abseil](#about) - [Quickstart](#quickstart) - [Building Abseil](#build) +- [Support](#support) - [Codemap](#codemap) +- [Releases](#releases) - [License](#license) - [Links](#links) @@ -42,47 +44,78 @@ the Abseil code, running tests, and getting a simple binary working. ## Building Abseil -[Bazel](https://bazel.build) is the official build system for Abseil, -which is supported on most major platforms (Linux, Windows, macOS, for example) -and compilers. See the [quickstart](https://abseil.io/docs/cpp/quickstart) for -more information on building Abseil using the Bazel build system. - - -If you require CMake support, please check the -[CMake build instructions](CMake/README.md). - +[Bazel](https://bazel.build) and [CMake](https://cmake.org/) are the official +build systems for Abseil. +See the [quickstart](https://abseil.io/docs/cpp/quickstart) for more information +on building Abseil using the Bazel build system. +If you require CMake support, please check the [CMake build +instructions](CMake/README.md) and [CMake +Quickstart](https://abseil.io/docs/cpp/quickstart-cmake). + + +## Support + +Abseil follows Google's [Foundational C++ Support +Policy](https://opensource.google/documentation/policies/cplusplus-support). See +[this +table](https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md) +for a list of currently supported versions compilers, platforms, and build +tools. + + ## Codemap Abseil contains the following C++ library components: -* [`base`](absl/base/) Abseil Fundamentals +* [`base`](absl/base/)
The `base` library contains initialization code and other code which all other Abseil code depends on. Code within `base` may not depend on any other code (other than the C++ standard library). * [`algorithm`](absl/algorithm/)
The `algorithm` library contains additions to the C++ `` library and container-based versions of such algorithms. +* [`cleanup`](absl/cleanup/) +
The `cleanup` library contains the control-flow-construct-like type + `absl::Cleanup` which is used for executing a callback on scope exit. * [`container`](absl/container/)
The `container` library contains additional STL-style containers, including Abseil's unordered "Swiss table" containers. +* [`crc`](absl/crc/) The `crc` library contains code for + computing error-detecting cyclic redundancy checks on data. * [`debugging`](absl/debugging/)
The `debugging` library contains code useful for enabling leak checks, and stacktrace and symbolization utilities. +* [`flags`](absl/flags/) +
The `flags` library contains code for handling command line flags for + libraries and binaries built with Abseil. * [`hash`](absl/hash/)
The `hash` library contains the hashing framework and default hash functor implementations for hashable types in Abseil. +* [`log`](absl/log/) +
The `log` library contains `LOG` and `CHECK` macros and facilities + for writing logged messages out to disk, `stderr`, or user-extensible + destinations. * [`memory`](absl/memory/) -
The `memory` library contains C++11-compatible versions of - `std::make_unique()` and related memory management facilities. +
The `memory` library contains memory management facilities that augment + C++'s `` library. * [`meta`](absl/meta/) -
The `meta` library contains C++11-compatible versions of type checks - available within C++14 and C++17 versions of the C++ `` library. +
The `meta` library contains type checks + similar to those available in the C++ `` library. * [`numeric`](absl/numeric/) -
The `numeric` library contains C++11-compatible 128-bit integers. +
The `numeric` library contains 128-bit integer types as well as + implementations of C++20's bitwise math functions. +* [`profiling`](absl/profiling/) +
The `profiling` library contains utility code for profiling C++ + entities. It is currently a private dependency of other Abseil libraries. +* [`random`](absl/random/) +
The `random` library contains functions for generating pseudorandom + values. +* [`status`](absl/status/) +
The `status` library contains abstractions for error handling, + specifically `absl::Status` and `absl::StatusOr`. * [`strings`](absl/strings/)
The `strings` library contains a variety of strings routines and - utilities, including a C++11-compatible version of the C++17 - `std::string_view` type. + utilities. * [`synchronization`](absl/synchronization/)
The `synchronization` library contains concurrency primitives (Abseil's `absl::Mutex` class, an alternative to `std::mutex`) and a variety of @@ -92,16 +125,27 @@ Abseil contains the following C++ library components: points in time, durations of time, and formatting and parsing time within time zones. * [`types`](absl/types/) -
The `types` library contains non-container utility types, like a - C++11-compatible version of the C++17 `std::optional` type. +
The `types` library contains non-container utility types. * [`utility`](absl/utility/)
The `utility` library contains utility and helper code. + +## Releases + +Abseil recommends users "live-at-head" (update to the latest commit from the +master branch as often as possible). However, we realize this philosophy doesn't +work for every project, so we also provide [Long Term Support +Releases](https://github.com/abseil/abseil-cpp/releases) to which we backport +fixes for severe bugs. See our [release +management](https://abseil.io/about/releases) document for more details. + + ## License The Abseil C++ library is licensed under the terms of the Apache license. See [LICENSE](LICENSE) for more information. + ## Links For more information about Abseil: diff --git a/UPGRADES.md b/UPGRADES.md index 35599d0878d..3cac141d0f4 100644 --- a/UPGRADES.md +++ b/UPGRADES.md @@ -1,6 +1,6 @@ # C++ Upgrade Tools -Abseil may occassionally release API-breaking changes. As noted in our +Abseil may occasionally release API-breaking changes. As noted in our [Compatibility Guidelines][compatibility-guide], we will aim to provide a tool to do the work of effecting such API-breaking changes, when absolutely necessary. diff --git a/WORKSPACE b/WORKSPACE deleted file mode 100644 index 572f5b1f7fa..00000000000 --- a/WORKSPACE +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright 2019 The Abseil Authors. -# -# 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 -# -# https://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. -# - -workspace(name = "com_google_absl") -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -# GoogleTest/GoogleMock framework. Used by most unit-tests. -http_archive( - name = "com_google_googletest", - urls = ["https://github.com/google/googletest/archive/b6cd405286ed8635ece71c72f118e659f4ade3fb.zip"], # 2019-01-07 - strip_prefix = "googletest-b6cd405286ed8635ece71c72f118e659f4ade3fb", - sha256 = "ff7a82736e158c077e76188232eac77913a15dac0b22508c390ab3f88e6d6d86", -) - -# Google benchmark. -http_archive( - name = "com_github_google_benchmark", - urls = ["https://github.com/google/benchmark/archive/16703ff83c1ae6d53e5155df3bb3ab0bc96083be.zip"], - strip_prefix = "benchmark-16703ff83c1ae6d53e5155df3bb3ab0bc96083be", - sha256 = "59f918c8ccd4d74b6ac43484467b500f1d64b40cc1010daa055375b322a43ba3", -) - -# C++ rules for Bazel. -http_archive( - name = "rules_cc", - sha256 = "67412176974bfce3f4cf8bdaff39784a72ed709fc58def599d1f68710b58d68b", - strip_prefix = "rules_cc-b7fe9697c0c76ab2fd431a891dbb9a6a32ed7c3e", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_cc/archive/b7fe9697c0c76ab2fd431a891dbb9a6a32ed7c3e.zip", - "https://github.com/bazelbuild/rules_cc/archive/b7fe9697c0c76ab2fd431a891dbb9a6a32ed7c3e.zip", - ], -) diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel index 853330d447e..980563a9f12 100644 --- a/absl/BUILD.bazel +++ b/absl/BUILD.bazel @@ -14,43 +14,49 @@ # limitations under the License. # -package(default_visibility = ["//visibility:public"]) +load("@bazel_skylib//lib:selects.bzl", "selects") -licenses(["notice"]) # Apache 2.0 +package(default_visibility = ["//visibility:private"]) -load(":compiler_config_setting.bzl", "create_llvm_config") +licenses(["notice"]) -create_llvm_config( - name = "llvm_compiler", +config_setting( + name = "clang_compiler", + flag_values = { + "@bazel_tools//tools/cpp:compiler": "clang", + }, visibility = [":__subpackages__"], ) config_setting( - name = "osx", - constraint_values = [ - "@bazel_tools//platforms:osx", - ], + name = "mingw_unspecified_compiler", + flag_values = { + "@bazel_tools//tools/cpp:compiler": "mingw", + }, + visibility = [":__subpackages__"], ) config_setting( - name = "ios", - constraint_values = [ - "@bazel_tools//platforms:ios", - ], + name = "mingw-gcc_compiler", + flag_values = { + "@bazel_tools//tools/cpp:compiler": "mingw-gcc", + }, + visibility = [":__subpackages__"], ) config_setting( - name = "windows", - values = { - "cpu": "x64_windows", - }, + name = "fuchsia", + constraint_values = [ + "@platforms//os:fuchsia", + ], visibility = [":__subpackages__"], ) -config_setting( - name = "ppc", - values = { - "cpu": "ppc", - }, +selects.config_setting_group( + name = "mingw_compiler", + match_any = [ + ":mingw_unspecified_compiler", + ":mingw-gcc_compiler", + ], visibility = [":__subpackages__"], ) diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt index 3e78397c976..810d7f31b9a 100644 --- a/absl/CMakeLists.txt +++ b/absl/CMakeLists.txt @@ -14,20 +14,31 @@ # limitations under the License. # - - add_subdirectory(base) add_subdirectory(algorithm) +add_subdirectory(cleanup) add_subdirectory(container) +add_subdirectory(crc) add_subdirectory(debugging) add_subdirectory(flags) +add_subdirectory(functional) add_subdirectory(hash) +add_subdirectory(log) add_subdirectory(memory) add_subdirectory(meta) add_subdirectory(numeric) +add_subdirectory(profiling) add_subdirectory(random) +add_subdirectory(status) add_subdirectory(strings) add_subdirectory(synchronization) add_subdirectory(time) add_subdirectory(types) add_subdirectory(utility) + +if (ABSL_BUILD_DLL) + absl_make_dll() + if ((BUILD_TESTING AND ABSL_BUILD_TESTING) OR ABSL_BUILD_TEST_HELPERS) + absl_make_dll(TEST ON) + endif() +endif() diff --git a/absl/abseil.podspec.gen.py b/absl/abseil.podspec.gen.py new file mode 100755 index 00000000000..e19f951193c --- /dev/null +++ b/absl/abseil.podspec.gen.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""This script generates abseil.podspec from all BUILD.bazel files. + +This is expected to run on abseil git repository with Bazel 1.0 on Linux. +It recursively analyzes BUILD.bazel files using query command of Bazel to +dump its build rules in XML format. From these rules, it constructs podspec +structure. +""" + +import argparse +import collections +import os +import re +import subprocess +import xml.etree.ElementTree + +# Template of root podspec. +SPEC_TEMPLATE = """ +# This file has been automatically generated from a script. +# Please make modifications to `abseil.podspec.gen.py` instead. +Pod::Spec.new do |s| + s.name = 'abseil' + s.version = '${version}' + s.summary = 'Abseil Common Libraries (C++) from Google' + s.homepage = 'https://abseil.io' + s.license = 'Apache License, Version 2.0' + s.authors = { 'Abseil Team' => 'abseil-io@googlegroups.com' } + s.source = { + :git => 'https://github.com/abseil/abseil-cpp.git', + :tag => '${tag}', + } + s.resource_bundles = { + s.module_name => 'PrivacyInfo.xcprivacy', + } + s.module_name = 'absl' + s.header_mappings_dir = 'absl' + s.header_dir = 'absl' + s.libraries = 'c++' + s.compiler_flags = '-Wno-everything' + s.pod_target_xcconfig = { + 'USER_HEADER_SEARCH_PATHS' => '$(inherited) "$(PODS_TARGET_SRCROOT)"', + 'USE_HEADERMAP' => 'NO', + 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + } + s.ios.deployment_target = '12.0' + s.osx.deployment_target = '10.13' + s.tvos.deployment_target = '12.0' + s.watchos.deployment_target = '4.0' + s.visionos.deployment_target = '1.0' + s.subspec 'xcprivacy' do |ss| + ss.resource_bundles = { + ss.module_name => 'PrivacyInfo.xcprivacy', + } + end +""" + +# Rule object representing the rule of Bazel BUILD. +Rule = collections.namedtuple( + "Rule", "type name package srcs hdrs textual_hdrs deps visibility testonly") + + +def get_elem_value(elem, name): + """Returns the value of XML element with the given name.""" + for child in elem: + if child.attrib.get("name") != name: + continue + if child.tag == "string": + return child.attrib.get("value") + if child.tag == "boolean": + return child.attrib.get("value") == "true" + if child.tag == "list": + return [nested_child.attrib.get("value") for nested_child in child] + raise "Cannot recognize tag: " + child.tag + return None + + +def normalize_paths(paths): + """Returns the list of normalized path.""" + # e.g. ["//absl/strings:dir/header.h"] -> ["absl/strings/dir/header.h"] + return [path.lstrip("/").replace(":", "/") for path in paths] + + +def parse_rule(elem, package): + """Returns a rule from bazel XML rule.""" + return Rule( + type=elem.attrib["class"], + name=get_elem_value(elem, "name"), + package=package, + srcs=normalize_paths(get_elem_value(elem, "srcs") or []), + hdrs=normalize_paths(get_elem_value(elem, "hdrs") or []), + textual_hdrs=normalize_paths(get_elem_value(elem, "textual_hdrs") or []), + deps=get_elem_value(elem, "deps") or [], + visibility=get_elem_value(elem, "visibility") or [], + testonly=get_elem_value(elem, "testonly") or False) + + +def read_build(package): + """Runs bazel query on given package file and returns all cc rules.""" + result = subprocess.check_output( + ["bazel", "query", package + ":all", "--output", "xml"]) + root = xml.etree.ElementTree.fromstring(result) + return [ + parse_rule(elem, package) + for elem in root + if elem.tag == "rule" and elem.attrib["class"].startswith("cc_") + ] + + +def collect_rules(root_path): + """Collects and returns all rules from root path recursively.""" + rules = [] + for cur, _, _ in os.walk(root_path): + build_path = os.path.join(cur, "BUILD.bazel") + if os.path.exists(build_path): + rules.extend(read_build("//" + cur)) + return rules + + +def relevant_rule(rule): + """Returns true if a given rule is relevant when generating a podspec.""" + return ( + # cc_library only (ignore cc_test, cc_binary) + rule.type == "cc_library" and + # ignore empty rule + (rule.hdrs + rule.textual_hdrs + rule.srcs) and + # ignore test-only rule + not rule.testonly) + + +def get_spec_var(depth): + """Returns the name of variable for spec with given depth.""" + return "s" if depth == 0 else "s{}".format(depth) + + +def get_spec_name(label): + """Converts the label of bazel rule to the name of podspec.""" + assert label.startswith("//absl/"), "{} doesn't start with //absl/".format( + label) + # e.g. //absl/apple/banana -> abseil/apple/banana + return "abseil/" + label[7:] + + +def write_podspec(f, rules, args): + """Writes a podspec from given rules and args.""" + rule_dir = build_rule_directory(rules)["abseil"] + # Write root part with given arguments + spec = re.sub(r"\$\{(\w+)\}", lambda x: args[x.group(1)], + SPEC_TEMPLATE).lstrip() + f.write(spec) + # Write all target rules + write_podspec_map(f, rule_dir, 0) + f.write("end\n") + + +def build_rule_directory(rules): + """Builds a tree-style rule directory from given rules.""" + rule_dir = {} + for rule in rules: + cur = rule_dir + for frag in get_spec_name(rule.package).split("/"): + cur = cur.setdefault(frag, {}) + cur[rule.name] = rule + return rule_dir + + +def write_podspec_map(f, cur_map, depth): + """Writes podspec from rule map recursively.""" + for key, value in sorted(cur_map.items()): + indent = " " * (depth + 1) + f.write("{indent}{var0}.subspec '{key}' do |{var1}|\n".format( + indent=indent, + key=key, + var0=get_spec_var(depth), + var1=get_spec_var(depth + 1))) + if isinstance(value, dict): + write_podspec_map(f, value, depth + 1) + else: + write_podspec_rule(f, value, depth + 1) + f.write("{indent}end\n".format(indent=indent)) + + +def write_podspec_rule(f, rule, depth): + """Writes podspec from given rule.""" + indent = " " * (depth + 1) + spec_var = get_spec_var(depth) + # Puts all files in hdrs, textual_hdrs, and srcs into source_files. + # Since CocoaPods treats header_files a bit differently from bazel, + # this won't generate a header_files field so that all source_files + # are considered as header files. + srcs = sorted(set(rule.hdrs + rule.textual_hdrs + rule.srcs)) + write_indented_list( + f, "{indent}{var}.source_files = ".format(indent=indent, var=spec_var), + srcs) + # Writes dependencies of this rule. + for dep in sorted(rule.deps): + name = get_spec_name(dep.replace(":", "/")) + f.write("{indent}{var}.dependency '{dep}'\n".format( + indent=indent, var=spec_var, dep=name)) + # Writes dependency to xcprivacy + f.write( + "{indent}{var}.dependency '{dep}'\n".format( + indent=indent, var=spec_var, dep="abseil/xcprivacy" + ) + ) + + +def write_indented_list(f, leading, values): + """Writes leading values in an indented style.""" + f.write(leading) + f.write((",\n" + " " * len(leading)).join("'{}'".format(v) for v in values)) + f.write("\n") + + +def generate(args): + """Generates a podspec file from all BUILD files under absl directory.""" + rules = filter(relevant_rule, collect_rules("absl")) + with open(args.output, "wt") as f: + write_podspec(f, rules, vars(args)) + + +def main(): + parser = argparse.ArgumentParser( + description="Generates abseil.podspec from BUILD.bazel") + parser.add_argument( + "-v", "--version", help="The version of podspec", required=True) + parser.add_argument( + "-t", + "--tag", + default=None, + help="The name of git tag (default: version)") + parser.add_argument( + "-o", + "--output", + default="abseil.podspec", + help="The name of output file (default: abseil.podspec)") + args = parser.parse_args() + if args.tag is None: + args.tag = args.version + generate(args) + + +if __name__ == "__main__": + main() diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel index 2ee8c09e626..10b9400bb55 100644 --- a/absl/algorithm/BUILD.bazel +++ b/absl/algorithm/BUILD.bazel @@ -14,7 +14,8 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -22,15 +23,27 @@ load( "ABSL_TEST_COPTS", ) -package(default_visibility = ["//visibility:public"]) +package( + default_visibility = ["//visibility:private"], + features = [ + "header_modules", + "layering_check", + "parse_headers", + ], +) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "algorithm", hdrs = ["algorithm.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + ], ) cc_test( @@ -41,20 +54,9 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":algorithm", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "algorithm_benchmark", - srcs = ["equal_benchmark.cc"], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - tags = ["benchmark"], - deps = [ - ":algorithm", - "//absl/base:core_headers", - "@com_github_google_benchmark//:benchmark_main", + "//absl/base:config", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) @@ -65,9 +67,13 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], deps = [ ":algorithm", + "//absl/base:config", "//absl/base:core_headers", + "//absl/base:hardening", + "//absl/base:iterator_traits_internal", "//absl/meta:type_traits", ], ) @@ -80,9 +86,13 @@ cc_test( deps = [ ":container", "//absl/base", + "//absl/base:config", "//absl/base:core_headers", "//absl/memory", + "//absl/meta:type_traits", + "//absl/random", "//absl/types:span", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt index 9fbe36f698f..cd7fa221ef7 100644 --- a/absl/algorithm/CMakeLists.txt +++ b/absl/algorithm/CMakeLists.txt @@ -21,6 +21,9 @@ absl_cc_library( "algorithm.h" COPTS ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers PUBLIC ) @@ -33,7 +36,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::algorithm - gmock_main + absl::config + GTest::gmock_main ) absl_cc_library( @@ -45,6 +49,9 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::algorithm + absl::hardening + absl::config + absl::iterator_traits_internal absl::core_headers absl::meta PUBLIC @@ -59,9 +66,11 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::algorithm_container - absl::base + absl::config absl::core_headers absl::memory + absl::meta + absl::random_random absl::span - gmock_main + GTest::gmock_main ) diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h index bb90215d80d..0b75908a053 100644 --- a/absl/algorithm/algorithm.h +++ b/absl/algorithm/algorithm.h @@ -26,93 +26,57 @@ #include #include -namespace absl { +#include "absl/base/config.h" +#include "absl/base/macros.h" -namespace algorithm_internal { +namespace absl { +ABSL_NAMESPACE_BEGIN -// Performs comparisons with operator==, similar to C++14's `std::equal_to<>`. -struct EqualTo { - template - bool operator()(const T& a, const U& b) const { - return a == b; - } -}; +// equal() +// rotate() +// +// Historical note: Abseil once provided implementations of these algorithms +// prior to their adoption in C++14. New code should prefer to use the std +// variants. +// +// See the documentation for the STL header for more information: +// https://en.cppreference.com/w/cpp/header/algorithm -template -bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, - InputIter2 last2, Pred pred, std::input_iterator_tag, - std::input_iterator_tag) { - while (true) { - if (first1 == last1) return first2 == last2; - if (first2 == last2) return false; - if (!pred(*first1, *first2)) return false; - ++first1; - ++first2; - } +template +ABSL_DEPRECATE_AND_INLINE() +constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) { + return std::equal(first1, last1, first2); } -template -bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, - InputIter2 last2, Pred&& pred, std::random_access_iterator_tag, - std::random_access_iterator_tag) { - return (last1 - first1 == last2 - first2) && - std::equal(first1, last1, first2, std::forward(pred)); +template +ABSL_DEPRECATE_AND_INLINE() +constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, + BinaryPredicate p) { + return std::equal(first1, last1, first2, p); } -// When we are using our own internal predicate that just applies operator==, we -// forward to the non-predicate form of std::equal. This enables an optimization -// in libstdc++ that can result in std::memcmp being used for integer types. -template -bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, - InputIter2 last2, algorithm_internal::EqualTo /* unused */, - std::random_access_iterator_tag, - std::random_access_iterator_tag) { - return (last1 - first1 == last2 - first2) && - std::equal(first1, last1, first2); +template +ABSL_DEPRECATE_AND_INLINE() +constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, + InputIt2 last2) { + return std::equal(first1, last1, first2, last2); } -template -It RotateImpl(It first, It middle, It last, std::true_type) { - return std::rotate(first, middle, last); +template +ABSL_DEPRECATE_AND_INLINE() +constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, + InputIt2 last2, BinaryPredicate p) { + return std::equal(first1, last1, first2, last2, p); } -template -It RotateImpl(It first, It middle, It last, std::false_type) { - std::rotate(first, middle, last); - return std::next(first, std::distance(middle, last)); +template +ABSL_DEPRECATE_AND_INLINE() +constexpr ForwardIt rotate(ForwardIt first, ForwardIt n_first, ForwardIt last) { + return std::rotate(first, n_first, last); } -} // namespace algorithm_internal - -// Compares the equality of two ranges specified by pairs of iterators, using -// the given predicate, returning true iff for each corresponding iterator i1 -// and i2 in the first and second range respectively, pred(*i1, *i2) == true -// -// This comparison takes at most min(`last1` - `first1`, `last2` - `first2`) -// invocations of the predicate. Additionally, if InputIter1 and InputIter2 are -// both random-access iterators, and `last1` - `first1` != `last2` - `first2`, -// then the predicate is never invoked and the function returns false. +// linear_search() // -// This is a C++11-compatible implementation of C++14 `std::equal`. See -// https://en.cppreference.com/w/cpp/algorithm/equal for more information. -template -bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, - InputIter2 last2, Pred&& pred) { - return algorithm_internal::EqualImpl( - first1, last1, first2, last2, std::forward(pred), - typename std::iterator_traits::iterator_category{}, - typename std::iterator_traits::iterator_category{}); -} - -// Performs comparison of two ranges specified by pairs of iterators using -// operator==. -template -bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, - InputIter2 last2) { - return absl::equal(first1, last1, first2, last2, - algorithm_internal::EqualTo{}); -} - // Performs a linear search for `value` using the iterator `first` up to // but not including `last`, returning true if [`first`, `last`) contains an // element equal to `value`. @@ -121,30 +85,12 @@ bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, // n = (`last` - `first`) comparisons. A linear search over short containers // may be faster than a binary search, even when the container is sorted. template -bool linear_search(InputIterator first, InputIterator last, - const EqualityComparable& value) { +constexpr bool linear_search(InputIterator first, InputIterator last, + const EqualityComparable& value) { return std::find(first, last, value) != last; } -// Performs a left rotation on a range of elements (`first`, `last`) such that -// `middle` is now the first element. `rotate()` returns an iterator pointing to -// the first element before rotation. This function is exactly the same as -// `std::rotate`, but fixes a bug in gcc -// <= 4.9 where `std::rotate` returns `void` instead of an iterator. -// -// The complexity of this algorithm is the same as that of `std::rotate`, but if -// `ForwardIterator` is not a random-access iterator, then `absl::rotate` -// performs an additional pass over the range to construct the return value. - -template -ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, - ForwardIterator last) { - return algorithm_internal::RotateImpl( - first, middle, last, - std::is_same()); -} - +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_ALGORITHM_ALGORITHM_H_ diff --git a/absl/algorithm/algorithm_test.cc b/absl/algorithm/algorithm_test.cc index 81fccb61353..1c1a3079f8a 100644 --- a/absl/algorithm/algorithm_test.cc +++ b/absl/algorithm/algorithm_test.cc @@ -14,138 +14,14 @@ #include "absl/algorithm/algorithm.h" -#include -#include +#include #include -#include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/config.h" namespace { -TEST(EqualTest, DefaultComparisonRandomAccess) { - std::vector v1{1, 2, 3}; - std::vector v2 = v1; - std::vector v3 = {1, 2}; - std::vector v4 = {1, 2, 4}; - - EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end())); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end())); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end())); -} - -TEST(EqualTest, DefaultComparison) { - std::list lst1{1, 2, 3}; - std::list lst2 = lst1; - std::list lst3{1, 2}; - std::list lst4{1, 2, 4}; - - EXPECT_TRUE(absl::equal(lst1.begin(), lst1.end(), lst2.begin(), lst2.end())); - EXPECT_FALSE(absl::equal(lst1.begin(), lst1.end(), lst3.begin(), lst3.end())); - EXPECT_FALSE(absl::equal(lst1.begin(), lst1.end(), lst4.begin(), lst4.end())); -} - -TEST(EqualTest, EmptyRange) { - std::vector v1{1, 2, 3}; - std::vector empty1; - std::vector empty2; - - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), empty1.begin(), empty1.end())); - EXPECT_FALSE(absl::equal(empty1.begin(), empty1.end(), v1.begin(), v1.end())); - EXPECT_TRUE( - absl::equal(empty1.begin(), empty1.end(), empty2.begin(), empty2.end())); -} - -TEST(EqualTest, MixedIterTypes) { - std::vector v1{1, 2, 3}; - std::list lst1{v1.begin(), v1.end()}; - std::list lst2{1, 2, 4}; - std::list lst3{1, 2}; - - EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), lst1.begin(), lst1.end())); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), lst2.begin(), lst2.end())); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), lst3.begin(), lst3.end())); -} - -TEST(EqualTest, MixedValueTypes) { - std::vector v1{1, 2, 3}; - std::vector v2{1, 2, 3}; - std::vector v3{1, 2}; - std::vector v4{1, 2, 4}; - - EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end())); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end())); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end())); -} - -TEST(EqualTest, WeirdIterators) { - std::vector v1{true, false}; - std::vector v2 = v1; - std::vector v3{true}; - std::vector v4{true, true, true}; - - EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end())); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end())); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end())); -} - -TEST(EqualTest, CustomComparison) { - int n[] = {1, 2, 3, 4}; - std::vector v1{&n[0], &n[1], &n[2]}; - std::vector v2 = v1; - std::vector v3{&n[0], &n[1], &n[3]}; - std::vector v4{&n[0], &n[1]}; - - auto eq = [](int* a, int* b) { return *a == *b; }; - - EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), eq)); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end(), eq)); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end(), eq)); -} - -TEST(EqualTest, MoveOnlyPredicate) { - std::vector v1{1, 2, 3}; - std::vector v2{4, 5, 6}; - - // move-only equality predicate - struct Eq { - Eq() = default; - Eq(Eq &&) = default; - Eq(const Eq &) = delete; - Eq &operator=(const Eq &) = delete; - bool operator()(const int a, const int b) const { return a == b; } - }; - - EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v1.begin(), v1.end(), Eq())); - EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), Eq())); -} - -struct CountingTrivialPred { - int* count; - bool operator()(int, int) const { - ++*count; - return true; - } -}; - -TEST(EqualTest, RandomAccessComplexity) { - std::vector v1{1, 1, 3}; - std::vector v2 = v1; - std::vector v3{1, 2}; - - do { - int count = 0; - absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), - CountingTrivialPred{&count}); - EXPECT_LE(count, 3); - } while (std::next_permutation(v2.begin(), v2.end())); - - int count = 0; - absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end(), - CountingTrivialPred{&count}); - EXPECT_EQ(count, 0); -} - class LinearSearchTest : public testing::Test { protected: LinearSearchTest() : container_{1, 2, 3} {} @@ -169,14 +45,16 @@ TEST_F(LinearSearchTest, linear_searchConst) { absl::linear_search(const_container->begin(), const_container->end(), 4)); } -TEST(RotateTest, Rotate) { - std::vector v{0, 1, 2, 3, 4}; - EXPECT_EQ(*absl::rotate(v.begin(), v.begin() + 2, v.end()), 0); - EXPECT_THAT(v, testing::ElementsAreArray({2, 3, 4, 0, 1})); +#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L - std::list l{0, 1, 2, 3, 4}; - EXPECT_EQ(*absl::rotate(l.begin(), std::next(l.begin(), 3), l.end()), 0); - EXPECT_THAT(l, testing::ElementsAreArray({3, 4, 0, 1, 2})); +TEST_F(LinearSearchTest, Constexpr) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::linear_search(kArray.begin(), kArray.end(), 3)); + static_assert(!absl::linear_search(kArray.begin(), kArray.end(), 4)); } +#endif // defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && + // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L + } // namespace diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index c84de461ac2..9d2b48e9f14 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -51,10 +52,22 @@ #include #include "absl/algorithm/algorithm.h" +#include "absl/base/config.h" +#include "absl/base/internal/hardening.h" +#include "absl/base/internal/iterator_traits.h" #include "absl/base/macros.h" #include "absl/meta/type_traits.h" +#ifdef __cpp_lib_span +#include // NOLINT(build/c++20) +#endif + namespace absl { +ABSL_NAMESPACE_BEGIN + +template +class Span; + namespace container_algorithm_internal { // NOTE: it is important to defer to ADL lookup for building with C++ modules, @@ -72,13 +85,12 @@ using ContainerIter = decltype(begin(std::declval())); // An MSVC bug involving template parameter substitution requires us to use // decltype() here instead of just std::pair. template -using ContainerIterPairType = - decltype(std::make_pair(ContainerIter(), ContainerIter())); +using ContainerIterPairType = decltype(std::make_pair( + std::declval>(), std::declval>())); template -using ContainerDifferenceType = - decltype(std::distance(std::declval>(), - std::declval>())); +using ContainerDifferenceType = decltype(std::distance( + std::declval>(), std::declval>())); template using ContainerPointerType = @@ -89,17 +101,55 @@ using ContainerPointerType = // lookup of std::begin and std::end, i.e. // using std::begin; // using std::end; -// std::foo(begin(c), end(c); +// std::foo(begin(c), end(c)); // becomes -// std::foo(container_algorithm_internal::begin(c), -// container_algorithm_internal::end(c)); +// std::foo(container_algorithm_internal::c_begin(c), +// container_algorithm_internal::c_end(c)); // These are meant for internal use only. template -ContainerIter c_begin(C& c) { return begin(c); } +constexpr ContainerIter c_begin(C& c) { + return begin(c); +} template -ContainerIter c_end(C& c) { return end(c); } +constexpr ContainerIter c_end(C& c) { + return end(c); +} + +// Helper to check that the `OutputRange` has enough space. +// Only performs the check if the iterators are ForwardIterators or better. +template +constexpr void AssertCopyNSize(InputSequence& input, Size n, + OutputRange& output) { + using InputIter = ContainerIter; + using OutputIter = ContainerIter; + + if constexpr (base_internal::IsAtLeastForwardIterator::value) { + base_internal::HardeningAssertLE( + n, std::distance(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input))); + } + if constexpr (base_internal::IsAtLeastForwardIterator::value) { + base_internal::HardeningAssertLE( + n, std::distance(container_algorithm_internal::c_begin(output), + container_algorithm_internal::c_end(output))); + } +} + +template +constexpr void AssertCopySize(InputSequence& input, OutputRange& output) { + using InputIter = ContainerIter; + using OutputIter = ContainerIter; + if constexpr (base_internal::IsAtLeastForwardIterator::value && + base_internal::IsAtLeastForwardIterator::value) { + base_internal::HardeningAssertLE( + std::distance(container_algorithm_internal::c_begin(input), + container_algorithm_internal::c_end(input)), + std::distance(container_algorithm_internal::c_begin(output), + container_algorithm_internal::c_end(output))); + } +} template struct IsUnorderedContainer : std::false_type {}; @@ -112,6 +162,77 @@ template struct IsUnorderedContainer> : std::true_type {}; +template +struct HasBeginEnd : std::false_type {}; + +template +struct HasBeginEnd()())), + decltype(container_algorithm_internal::end( + std::declval()()))>> + : std::true_type {}; + +// We don't support multidimensional arrays yet +template +using IsMultidimensionalArray = std::is_array>; + +template +struct IsIterator : std::false_type {}; + +template +struct IsIterator< + Iter, std::void_t::iterator_category>> + : std::true_type {}; + +template +using ResultOfRangeToIteratorTransfer = + std::enable_if_t>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t>::value, + std::decay_t>; + +// Similar to std::is_pointer, but for testing if a type is a span. +// +// Note that subclasses of spans do not automatically qualify as spans, as they +// may deviate from the ownership assumption of a span. +template +struct IsSpan + : std::conditional_t>, + std::false_type, IsSpan>> {}; + +template +struct IsSpan> : std::true_type {}; + +#ifdef __cpp_lib_span +template +struct IsSpan> : std::true_type {}; +#endif + +// Indicates whether the given type is safe to pass as a sink to a function such +// as absl::c_fill(). Similar idea as std::ranges::borrowed_range. +// +// We are deliberately conservative here and only support lvalues and spans for +// now, in order to avoid divergence from C++17 or potentially unforeseen +// consequences. If needed in the future, we can probably extend this to all +// types that satisfy std::ranges::borrowed_range. +template +using IsPermissibleDestinationRange = + std::conditional_t, std::true_type, + IsSpan>; + +template +using ResultOfRangeToRangeTransfer = + std::enable_if_t>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t>::value && + container_algorithm_internal:: + IsPermissibleDestinationRange::value, + void>; + } // namespace container_algorithm_internal // PUBLIC API @@ -124,11 +245,13 @@ struct IsUnorderedContainer> // // Container-based version of absl::linear_search() for performing a linear // search within a container. +// +// For a generalization that uses a predicate, see absl::c_any_of(). template -bool c_linear_search(const C& c, EqualityComparable&& value) { - return linear_search(container_algorithm_internal::c_begin(c), - container_algorithm_internal::c_end(c), - std::forward(value)); +constexpr bool c_linear_search(const C& c, EqualityComparable&& value) { + return absl::linear_search(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), + std::forward(value)); } //------------------------------------------------------------------------------ @@ -140,8 +263,8 @@ bool c_linear_search(const C& c, EqualityComparable&& value) { // Container-based version of the `std::distance()` function to // return the number of elements within a container. template -container_algorithm_internal::ContainerDifferenceType c_distance( - const C& c) { +constexpr container_algorithm_internal::ContainerDifferenceType +c_distance(const C& c) { return std::distance(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } @@ -153,9 +276,9 @@ container_algorithm_internal::ContainerDifferenceType c_distance( // c_all_of() // // Container-based version of the `std::all_of()` function to -// test a condition on all elements within a container. +// test if all elements within a container satisfy a condition. template -bool c_all_of(const C& c, Pred&& pred) { +constexpr bool c_all_of(const C& c, Pred&& pred) { return std::all_of(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -166,7 +289,7 @@ bool c_all_of(const C& c, Pred&& pred) { // Container-based version of the `std::any_of()` function to // test if any element in a container fulfills a condition. template -bool c_any_of(const C& c, Pred&& pred) { +constexpr bool c_any_of(const C& c, Pred&& pred) { return std::any_of(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -175,9 +298,9 @@ bool c_any_of(const C& c, Pred&& pred) { // c_none_of() // // Container-based version of the `std::none_of()` function to -// test if no elements in a container fulfil a condition. +// test if no elements in a container fulfill a condition. template -bool c_none_of(const C& c, Pred&& pred) { +constexpr bool c_none_of(const C& c, Pred&& pred) { return std::none_of(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -188,7 +311,7 @@ bool c_none_of(const C& c, Pred&& pred) { // Container-based version of the `std::for_each()` function to // apply a function to a container's elements. template -decay_t c_for_each(C&& c, Function&& f) { +constexpr std::decay_t c_for_each(C&& c, Function&& f) { return std::for_each(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(f)); @@ -199,18 +322,30 @@ decay_t c_for_each(C&& c, Function&& f) { // Container-based version of the `std::find()` function to find // the first element containing the passed value within a container value. template -container_algorithm_internal::ContainerIter c_find(C& c, T&& value) { +constexpr container_algorithm_internal::ContainerIter c_find(C& c, + T&& value) { return std::find(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(value)); } +// c_contains() +// +// Container-based version of the `std::ranges::contains()` C++23 +// function to search a container for a value. +template +constexpr bool c_contains(const Sequence& sequence, T&& value) { + return absl::c_find(sequence, std::forward(value)) != + container_algorithm_internal::c_end(sequence); +} + // c_find_if() // // Container-based version of the `std::find_if()` function to find // the first element in a container matching the given condition. template -container_algorithm_internal::ContainerIter c_find_if(C& c, Pred&& pred) { +constexpr container_algorithm_internal::ContainerIter c_find_if( + C& c, Pred&& pred) { return std::find_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -221,8 +356,8 @@ container_algorithm_internal::ContainerIter c_find_if(C& c, Pred&& pred) { // Container-based version of the `std::find_if_not()` function to // find the first element in a container not matching the given condition. template -container_algorithm_internal::ContainerIter c_find_if_not(C& c, - Pred&& pred) { +constexpr container_algorithm_internal::ContainerIter c_find_if_not( + C& c, Pred&& pred) { return std::find_if_not(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -233,7 +368,7 @@ container_algorithm_internal::ContainerIter c_find_if_not(C& c, // Container-based version of the `std::find_end()` function to // find the last subsequence within a container. template -container_algorithm_internal::ContainerIter c_find_end( +constexpr container_algorithm_internal::ContainerIter c_find_end( Sequence1& sequence, Sequence2& subsequence) { return std::find_end(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), @@ -244,7 +379,7 @@ container_algorithm_internal::ContainerIter c_find_end( // Overload of c_find_end() for using a predicate evaluation other than `==` as // the function's test condition. template -container_algorithm_internal::ContainerIter c_find_end( +constexpr container_algorithm_internal::ContainerIter c_find_end( Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) { return std::find_end(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), @@ -256,10 +391,11 @@ container_algorithm_internal::ContainerIter c_find_end( // c_find_first_of() // // Container-based version of the `std::find_first_of()` function to -// find the first elements in an ordered set within a container. +// find the first element within the container that is also within the options +// container. template -container_algorithm_internal::ContainerIter c_find_first_of(C1& container, - C2& options) { +constexpr container_algorithm_internal::ContainerIter c_find_first_of( + C1& container, const C2& options) { return std::find_first_of(container_algorithm_internal::c_begin(container), container_algorithm_internal::c_end(container), container_algorithm_internal::c_begin(options), @@ -269,8 +405,8 @@ container_algorithm_internal::ContainerIter c_find_first_of(C1& container, // Overload of c_find_first_of() for using a predicate evaluation other than // `==` as the function's test condition. template -container_algorithm_internal::ContainerIter c_find_first_of( - C1& container, C2& options, BinaryPredicate&& pred) { +constexpr container_algorithm_internal::ContainerIter c_find_first_of( + C1& container, const C2& options, BinaryPredicate&& pred) { return std::find_first_of(container_algorithm_internal::c_begin(container), container_algorithm_internal::c_end(container), container_algorithm_internal::c_begin(options), @@ -283,7 +419,7 @@ container_algorithm_internal::ContainerIter c_find_first_of( // Container-based version of the `std::adjacent_find()` function to // find equal adjacent elements within a container. template -container_algorithm_internal::ContainerIter c_adjacent_find( +constexpr container_algorithm_internal::ContainerIter c_adjacent_find( Sequence& sequence) { return std::adjacent_find(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); @@ -292,7 +428,7 @@ container_algorithm_internal::ContainerIter c_adjacent_find( // Overload of c_adjacent_find() for using a predicate evaluation other than // `==` as the function's test condition. template -container_algorithm_internal::ContainerIter c_adjacent_find( +constexpr container_algorithm_internal::ContainerIter c_adjacent_find( Sequence& sequence, BinaryPredicate&& pred) { return std::adjacent_find(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), @@ -304,8 +440,8 @@ container_algorithm_internal::ContainerIter c_adjacent_find( // Container-based version of the `std::count()` function to count // values that match within a container. template -container_algorithm_internal::ContainerDifferenceType c_count( - const C& c, T&& value) { +constexpr container_algorithm_internal::ContainerDifferenceType +c_count(const C& c, T&& value) { return std::count(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(value)); @@ -316,8 +452,8 @@ container_algorithm_internal::ContainerDifferenceType c_count( // Container-based version of the `std::count_if()` function to // count values matching a condition within a container. template -container_algorithm_internal::ContainerDifferenceType c_count_if( - const C& c, Pred&& pred) { +constexpr container_algorithm_internal::ContainerDifferenceType +c_count_if(const C& c, Pred&& pred) { return std::count_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -326,60 +462,50 @@ container_algorithm_internal::ContainerDifferenceType c_count_if( // c_mismatch() // // Container-based version of the `std::mismatch()` function to -// return the first element where two ordered containers differ. +// return the first element where two ordered containers differ. Applies `==` to +// the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). template -container_algorithm_internal::ContainerIterPairType +constexpr container_algorithm_internal::ContainerIterPairType c_mismatch(C1& c1, C2& c2) { return std::mismatch(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), - container_algorithm_internal::c_begin(c2)); + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2)); } // Overload of c_mismatch() for using a predicate evaluation other than `==` as -// the function's test condition. +// the function's test condition. Applies `pred`to the first N elements of `c1` +// and `c2`, where N = min(size(c1), size(c2)). template -container_algorithm_internal::ContainerIterPairType -c_mismatch(C1& c1, C2& c2, BinaryPredicate&& pred) { +constexpr container_algorithm_internal::ContainerIterPairType +c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) { return std::mismatch(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), - std::forward(pred)); + container_algorithm_internal::c_end(c2), pred); } // c_equal() // // Container-based version of the `std::equal()` function to // test whether two containers are equal. -// -// NOTE: the semantics of c_equal() are slightly different than those of -// equal(): while the latter iterates over the second container only up to the -// size of the first container, c_equal() also checks whether the container -// sizes are equal. This better matches expectations about c_equal() based on -// its signature. -// -// Example: -// vector v1 = <1, 2, 3>; -// vector v2 = <1, 2, 3, 4>; -// equal(std::begin(v1), std::end(v1), std::begin(v2)) returns true -// c_equal(v1, v2) returns false - template -bool c_equal(const C1& c1, const C2& c2) { - return ((c1.size() == c2.size()) && - std::equal(container_algorithm_internal::c_begin(c1), - container_algorithm_internal::c_end(c1), - container_algorithm_internal::c_begin(c2))); +constexpr bool c_equal(const C1& c1, const C2& c2) { + return std::equal(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2)); } // Overload of c_equal() for using a predicate evaluation other than `==` as // the function's test condition. template -bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) { - return ((c1.size() == c2.size()) && - std::equal(container_algorithm_internal::c_begin(c1), - container_algorithm_internal::c_end(c1), - container_algorithm_internal::c_begin(c2), - std::forward(pred))); +constexpr bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) { + return std::equal(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), + std::forward(pred)); } // c_is_permutation() @@ -387,21 +513,22 @@ bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) { // Container-based version of the `std::is_permutation()` function // to test whether a container is a permutation of another. template -bool c_is_permutation(const C1& c1, const C2& c2) { - using std::begin; - using std::end; - return c1.size() == c2.size() && - std::is_permutation(begin(c1), end(c1), begin(c2)); +constexpr bool c_is_permutation(const C1& c1, const C2& c2) { + return std::is_permutation(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2)); } // Overload of c_is_permutation() for using a predicate evaluation other than // `==` as the function's test condition. template -bool c_is_permutation(const C1& c1, const C2& c2, BinaryPredicate&& pred) { - using std::begin; - using std::end; - return c1.size() == c2.size() && - std::is_permutation(begin(c1), end(c1), begin(c2), +constexpr bool c_is_permutation(const C1& c1, const C2& c2, + BinaryPredicate&& pred) { + return std::is_permutation(container_algorithm_internal::c_begin(c1), + container_algorithm_internal::c_end(c1), + container_algorithm_internal::c_begin(c2), + container_algorithm_internal::c_end(c2), std::forward(pred)); } @@ -410,7 +537,7 @@ bool c_is_permutation(const C1& c1, const C2& c2, BinaryPredicate&& pred) { // Container-based version of the `std::search()` function to search // a container for a subsequence. template -container_algorithm_internal::ContainerIter c_search( +constexpr container_algorithm_internal::ContainerIter c_search( Sequence1& sequence, Sequence2& subsequence) { return std::search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), @@ -421,7 +548,7 @@ container_algorithm_internal::ContainerIter c_search( // Overload of c_search() for using a predicate evaluation other than // `==` as the function's test condition. template -container_algorithm_internal::ContainerIter c_search( +constexpr container_algorithm_internal::ContainerIter c_search( Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) { return std::search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), @@ -430,12 +557,33 @@ container_algorithm_internal::ContainerIter c_search( std::forward(pred)); } +// c_contains_subrange() +// +// Container-based version of the `std::ranges::contains_subrange()` +// C++23 function to search a container for a subsequence. +template +constexpr bool c_contains_subrange(Sequence1& sequence, + Sequence2& subsequence) { + return absl::c_search(sequence, subsequence) != + container_algorithm_internal::c_end(sequence); +} + +// Overload of c_contains_subrange() for using a predicate evaluation other than +// `==` as the function's test condition. +template +constexpr bool c_contains_subrange(Sequence1& sequence, Sequence2& subsequence, + BinaryPredicate&& pred) { + return absl::c_search(sequence, subsequence, + std::forward(pred)) != + container_algorithm_internal::c_end(sequence); +} + // c_search_n() // // Container-based version of the `std::search_n()` function to // search a container for the first sequence of N elements. template -container_algorithm_internal::ContainerIter c_search_n( +constexpr container_algorithm_internal::ContainerIter c_search_n( Sequence& sequence, Size count, T&& value) { return std::search_n(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), count, @@ -446,7 +594,7 @@ container_algorithm_internal::ContainerIter c_search_n( // `==` as the function's test condition. template -container_algorithm_internal::ContainerIter c_search_n( +constexpr container_algorithm_internal::ContainerIter c_search_n( Sequence& sequence, Size count, T&& value, BinaryPredicate&& pred) { return std::search_n(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), count, @@ -463,9 +611,30 @@ container_algorithm_internal::ContainerIter c_search_n( // Container-based version of the `std::copy()` function to copy a // container's elements into an iterator. template -OutputIterator c_copy(const InputSequence& input, OutputIterator output) { +constexpr container_algorithm_internal::ResultOfRangeToIteratorTransfer< + InputSequence, OutputIterator> +c_copy(const InputSequence& input, OutputIterator&& output) { return std::copy(container_algorithm_internal::c_begin(input), - container_algorithm_internal::c_end(input), output); + container_algorithm_internal::c_end(input), + std::forward(output)); +} + +// Copies elements from `input` to `output`. `absl::c_copy(input, output)` is +// equivalent to `std::copy(std::begin(input), std::end(input), +// std::begin(output))`. +// +// The `output` container must be large enough to hold all elements of `input`; +// this function does not resize `output`. + +// If `std::size(input) > std::size(output)`, behavior is undefined. +// If `std::size(output) > std::size(input)`, only `std::size(input)` elements +// are copied, and `output` is not truncated. +template +constexpr container_algorithm_internal::ResultOfRangeToRangeTransfer< + InputSequence, OutputRange> +c_copy(const InputSequence& input, OutputRange&& output) { + container_algorithm_internal::AssertCopySize(input, output); + absl::c_copy(input, container_algorithm_internal::c_begin(output)); } // c_copy_n() @@ -473,8 +642,30 @@ OutputIterator c_copy(const InputSequence& input, OutputIterator output) { // Container-based version of the `std::copy_n()` function to copy a // container's first N elements into an iterator. template -OutputIterator c_copy_n(const C& input, Size n, OutputIterator output) { - return std::copy_n(container_algorithm_internal::c_begin(input), n, output); +constexpr container_algorithm_internal::ResultOfRangeToIteratorTransfer< + C, OutputIterator> +c_copy_n(const C& input, Size n, OutputIterator&& output) { + return std::copy_n(container_algorithm_internal::c_begin(input), n, + std::forward(output)); +} + +// Copies the first `n` elements from `input` to `output`. +// `absl::c_copy_n(input, n, output)` is equivalent to +// `std::copy_n(std::begin(input), n, std::begin(output))`. +// +// The `output` container must be large enough to hold N elements; this function +// does not resize `output`. +// +// If `n > std::size(output)` or `n > std::size(input)`, behavior is +// undefined. +// If `std::size(output) > n`, only `n` elements are copied, and `output` is not +// truncated. +template +constexpr container_algorithm_internal::ResultOfRangeToRangeTransfer< + C, OutputRange> +c_copy_n(const C& input, Size n, OutputRange&& output) { + container_algorithm_internal::AssertCopyNSize(input, n, output); + absl::c_copy_n(input, n, container_algorithm_internal::c_begin(output)); } // c_copy_if() @@ -482,8 +673,8 @@ OutputIterator c_copy_n(const C& input, Size n, OutputIterator output) { // Container-based version of the `std::copy_if()` function to copy // a container's elements satisfying some condition into an iterator. template -OutputIterator c_copy_if(const InputSequence& input, OutputIterator output, - Pred&& pred) { +constexpr OutputIterator c_copy_if(const InputSequence& input, + OutputIterator output, Pred&& pred) { return std::copy_if(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output, std::forward(pred)); @@ -494,8 +685,8 @@ OutputIterator c_copy_if(const InputSequence& input, OutputIterator output, // Container-based version of the `std::copy_backward()` function to // copy a container's elements in reverse order into an iterator. template -BidirectionalIterator c_copy_backward(const C& src, - BidirectionalIterator dest) { +constexpr BidirectionalIterator c_copy_backward(const C& src, + BidirectionalIterator dest) { return std::copy_backward(container_algorithm_internal::c_begin(src), container_algorithm_internal::c_end(src), dest); } @@ -505,9 +696,26 @@ BidirectionalIterator c_copy_backward(const C& src, // Container-based version of the `std::move()` function to move // a container's elements into an iterator. template -OutputIterator c_move(C&& src, OutputIterator dest) { +constexpr container_algorithm_internal::ResultOfRangeToIteratorTransfer< + C, OutputIterator> +c_move(C&& src, OutputIterator&& dest) { return std::move(container_algorithm_internal::c_begin(src), - container_algorithm_internal::c_end(src), dest); + container_algorithm_internal::c_end(src), + std::forward(dest)); +} + +// Moves elements from `src` to `dest`. `absl::c_move(src, dest)` is +// equivalent to `std::move(std::begin(src), std::end(src), std::begin(dest))`. +// +// The `dest` container must be large enough to hold all elements of `src`; +// this function does not resize `dest`. +template +constexpr container_algorithm_internal::ResultOfRangeToRangeTransfer< + C, OutputRange> +c_move(C&& src, OutputRange&& dest) { + container_algorithm_internal::AssertCopySize(src, dest); + absl::c_move(std::forward(src), + container_algorithm_internal::c_begin(dest)); } // c_move_backward() @@ -515,7 +723,8 @@ OutputIterator c_move(C&& src, OutputIterator dest) { // Container-based version of the `std::move_backward()` function to // move a container's elements into an iterator in reverse order. template -BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { +constexpr BidirectionalIterator c_move_backward(C&& src, + BidirectionalIterator dest) { return std::move_backward(container_algorithm_internal::c_begin(src), container_algorithm_internal::c_end(src), dest); } @@ -523,12 +732,21 @@ BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { // c_swap_ranges() // // Container-based version of the `std::swap_ranges()` function to -// swap a container's elements with another container's elements. +// swap a container's elements with another container's elements. Swaps the +// first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). template -container_algorithm_internal::ContainerIter c_swap_ranges(C1& c1, C2& c2) { - return std::swap_ranges(container_algorithm_internal::c_begin(c1), - container_algorithm_internal::c_end(c1), - container_algorithm_internal::c_begin(c2)); +constexpr container_algorithm_internal::ContainerIter c_swap_ranges( + C1& c1, C2& c2) { + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + using std::swap; + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + swap(*first1, *first2); + } + return first2; } // c_transform() @@ -538,24 +756,109 @@ container_algorithm_internal::ContainerIter c_swap_ranges(C1& c1, C2& c2) { // result in an iterator pointing to the last transformed element in the output // range. template -OutputIterator c_transform(const InputSequence& input, OutputIterator output, - UnaryOp&& unary_op) { +constexpr container_algorithm_internal::ResultOfRangeToIteratorTransfer< + InputSequence, OutputIterator> +c_transform(const InputSequence& input, OutputIterator&& output, + UnaryOp&& unary_op) { return std::transform(container_algorithm_internal::c_begin(input), - container_algorithm_internal::c_end(input), output, + container_algorithm_internal::c_end(input), + std::forward(output), std::forward(unary_op)); } +// Performs a transformation using a unary predicate. Stores the result in +// `output`. `absl::c_transform(input, output, unary_op)` is equivalent to +// `std::transform(std::begin(input), std::end(input), std::begin(output), +// unary_op)`. +// +// The `output` container must be large enough to hold all elements of `input`; +// this function does not resize `output`. +template +constexpr container_algorithm_internal::ResultOfRangeToRangeTransfer< + InputSequence, OutputRange> +c_transform(const InputSequence& input, OutputRange&& output, + UnaryOp&& unary_op) { + container_algorithm_internal::AssertCopySize(input, output); + absl::c_transform( + input, + container_algorithm_internal::c_begin(std::forward(output)), + std::forward(unary_op)); +} + // Overload of c_transform() for performing a transformation using a binary -// predicate. +// predicate. Applies `binary_op` to the first N elements of `c1` and `c2`, +// where N = min(size(c1), size(c2)). template -OutputIterator c_transform(const InputSequence1& input1, - const InputSequence2& input2, OutputIterator output, - BinaryOp&& binary_op) { - return std::transform(container_algorithm_internal::c_begin(input1), - container_algorithm_internal::c_end(input1), - container_algorithm_internal::c_begin(input2), output, - std::forward(binary_op)); +constexpr container_algorithm_internal::ResultOfRangeToIteratorTransfer< + InputSequence1, OutputIterator> +c_transform(const InputSequence1& input1, const InputSequence2& input2, + OutputIterator&& output, BinaryOp&& binary_op) { + auto first1 = container_algorithm_internal::c_begin(input1); + auto last1 = container_algorithm_internal::c_end(input1); + auto first2 = container_algorithm_internal::c_begin(input2); + auto last2 = container_algorithm_internal::c_end(input2); + std::decay_t out = std::forward(output); + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2, ++out) { + *out = binary_op(*first1, *first2); + } + return out; +} + +// Performs a transformation using a binary predicate. Stores the result in +// `output`. Applies `binary_op` to the first N elements of `input1` and +// `input2`, where N = min(size(input1), size(input2)). +// +// The `output` container must be large enough to hold all N elements; +// this function does not resize `output`. +template +constexpr std::common_type_t< + container_algorithm_internal::ResultOfRangeToRangeTransfer, + container_algorithm_internal::ResultOfRangeToRangeTransfer> +c_transform(const InputSequence1& input1, const InputSequence2& input2, + OutputRange&& output, BinaryOp&& binary_op) { + using InputIter1 = + container_algorithm_internal::ContainerIter; + using InputIter2 = + container_algorithm_internal::ContainerIter; + using OutputIter = container_algorithm_internal::ContainerIter; + if constexpr (base_internal::IsAtLeastForwardIterator::value) { + constexpr bool input1_has_size = + base_internal::IsAtLeastForwardIterator::value; + constexpr bool input2_has_size = + base_internal::IsAtLeastForwardIterator::value; + auto output_size = + std::distance(container_algorithm_internal::c_begin(output), + container_algorithm_internal::c_end(output)); + + if constexpr (input1_has_size && input2_has_size) { + base_internal::HardeningAssertLE( + (std::min)(std::distance( + container_algorithm_internal::c_begin(input1), + container_algorithm_internal::c_end(input1)), + std::distance( + container_algorithm_internal::c_begin(input2), + container_algorithm_internal::c_end(input2))), + output_size); + } else if constexpr (input1_has_size) { + base_internal::HardeningAssertLE( + std::distance(container_algorithm_internal::c_begin(input1), + container_algorithm_internal::c_end(input1)), + output_size); + } else if constexpr (input2_has_size) { + base_internal::HardeningAssertLE( + std::distance(container_algorithm_internal::c_begin(input2), + container_algorithm_internal::c_end(input2)), + output_size); + } + } + absl::c_transform( + input1, input2, + container_algorithm_internal::c_begin(std::forward(output)), + std::forward(binary_op)); } // c_replace() @@ -564,7 +867,8 @@ OutputIterator c_transform(const InputSequence1& input1, // replace a container's elements of some value with a new value. The container // is modified in place. template -void c_replace(Sequence& sequence, const T& old_value, const T& new_value) { +constexpr void c_replace(Sequence& sequence, const T& old_value, + const T& new_value) { std::replace(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), old_value, new_value); @@ -576,7 +880,7 @@ void c_replace(Sequence& sequence, const T& old_value, const T& new_value) { // replace a container's elements of some value with a new value based on some // condition. The container is modified in place. template -void c_replace_if(C& c, Pred&& pred, T&& new_value) { +constexpr void c_replace_if(C& c, Pred&& pred, T&& new_value) { std::replace_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred), std::forward(new_value)); @@ -588,8 +892,8 @@ void c_replace_if(C& c, Pred&& pred, T&& new_value) { // replace a container's elements of some value with a new value and return the // results within an iterator. template -OutputIterator c_replace_copy(const C& c, OutputIterator result, T&& old_value, - T&& new_value) { +constexpr OutputIterator c_replace_copy(const C& c, OutputIterator result, + T&& old_value, T&& new_value) { return std::replace_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, std::forward(old_value), @@ -602,12 +906,11 @@ OutputIterator c_replace_copy(const C& c, OutputIterator result, T&& old_value, // to replace a container's elements of some value with a new value based on // some condition, and return the results within an iterator. template -OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, - T&& new_value) { +constexpr OutputIterator c_replace_copy_if(const C& c, OutputIterator result, + Pred&& pred, const T& new_value) { return std::replace_copy_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, - std::forward(pred), - std::forward(new_value)); + std::forward(pred), new_value); } // c_fill() @@ -615,9 +918,11 @@ OutputIterator c_replace_copy_if(const C& c, OutputIterator result, Pred&& pred, // Container-based version of the `std::fill()` function to fill a // container with some value. template -void c_fill(C& c, T&& value) { +constexpr std::enable_if_t< + container_algorithm_internal::IsPermissibleDestinationRange::value, void> +c_fill(C&& c, const T& value) { std::fill(container_algorithm_internal::c_begin(c), - container_algorithm_internal::c_end(c), std::forward(value)); + container_algorithm_internal::c_end(c), value); } // c_fill_n() @@ -625,9 +930,10 @@ void c_fill(C& c, T&& value) { // Container-based version of the `std::fill_n()` function to fill // the first N elements in a container with some value. template -void c_fill_n(C& c, Size n, T&& value) { - std::fill_n(container_algorithm_internal::c_begin(c), n, - std::forward(value)); +constexpr std::enable_if_t< + container_algorithm_internal::IsPermissibleDestinationRange::value, void> +c_fill_n(C&& c, Size n, const T& value) { + std::fill_n(container_algorithm_internal::c_begin(c), n, value); } // c_generate() @@ -635,7 +941,7 @@ void c_fill_n(C& c, Size n, T&& value) { // Container-based version of the `std::generate()` function to // assign a container's elements to the values provided by the given generator. template -void c_generate(C& c, Generator&& gen) { +constexpr void c_generate(C& c, Generator&& gen) { std::generate(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(gen)); @@ -647,8 +953,8 @@ void c_generate(C& c, Generator&& gen) { // assign a container's first N elements to the values provided by the given // generator. template -container_algorithm_internal::ContainerIter c_generate_n(C& c, Size n, - Generator&& gen) { +constexpr container_algorithm_internal::ContainerIter c_generate_n( + C& c, Size n, Generator&& gen) { return std::generate_n(container_algorithm_internal::c_begin(c), n, std::forward(gen)); } @@ -664,10 +970,11 @@ container_algorithm_internal::ContainerIter c_generate_n(C& c, Size n, // copy a container's elements while removing any elements matching the given // `value`. template -OutputIterator c_remove_copy(const C& c, OutputIterator result, T&& value) { +constexpr OutputIterator c_remove_copy(const C& c, OutputIterator result, + const T& value) { return std::remove_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, - std::forward(value)); + value); } // c_remove_copy_if() @@ -676,8 +983,8 @@ OutputIterator c_remove_copy(const C& c, OutputIterator result, T&& value) { // to copy a container's elements while removing any elements matching the given // condition. template -OutputIterator c_remove_copy_if(const C& c, OutputIterator result, - Pred&& pred) { +constexpr OutputIterator c_remove_copy_if(const C& c, OutputIterator result, + Pred&& pred) { return std::remove_copy_if(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, std::forward(pred)); @@ -689,7 +996,7 @@ OutputIterator c_remove_copy_if(const C& c, OutputIterator result, // copy a container's elements while removing any elements containing duplicate // values. template -OutputIterator c_unique_copy(const C& c, OutputIterator result) { +constexpr OutputIterator c_unique_copy(const C& c, OutputIterator result) { return std::unique_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result); } @@ -697,8 +1004,8 @@ OutputIterator c_unique_copy(const C& c, OutputIterator result) { // Overload of c_unique_copy() for using a predicate evaluation other than // `==` for comparing uniqueness of the element values. template -OutputIterator c_unique_copy(const C& c, OutputIterator result, - BinaryPredicate&& pred) { +constexpr OutputIterator c_unique_copy(const C& c, OutputIterator result, + BinaryPredicate&& pred) { return std::unique_copy(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), result, std::forward(pred)); @@ -709,7 +1016,7 @@ OutputIterator c_unique_copy(const C& c, OutputIterator result, // Container-based version of the `std::reverse()` function to // reverse a container's elements. template -void c_reverse(Sequence& sequence) { +constexpr void c_reverse(Sequence& sequence) { std::reverse(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } @@ -719,7 +1026,8 @@ void c_reverse(Sequence& sequence) { // Container-based version of the `std::reverse()` function to // reverse a container's elements and write them to an iterator range. template -OutputIterator c_reverse_copy(const C& sequence, OutputIterator result) { +constexpr OutputIterator c_reverse_copy(const C& sequence, + OutputIterator result) { return std::reverse_copy(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), result); @@ -732,9 +1040,9 @@ OutputIterator c_reverse_copy(const C& sequence, OutputIterator result) { // the first element in the container. template > -Iterator c_rotate(C& sequence, Iterator middle) { - return absl::rotate(container_algorithm_internal::c_begin(sequence), middle, - container_algorithm_internal::c_end(sequence)); +constexpr Iterator c_rotate(C& sequence, Iterator middle) { + return std::rotate(container_algorithm_internal::c_begin(sequence), middle, + container_algorithm_internal::c_end(sequence)); } // c_rotate_copy() @@ -743,7 +1051,7 @@ Iterator c_rotate(C& sequence, Iterator middle) { // shift a container's elements leftward such that the `middle` element becomes // the first element in a new iterator range. template -OutputIterator c_rotate_copy( +constexpr OutputIterator c_rotate_copy( const C& sequence, container_algorithm_internal::ContainerIter middle, OutputIterator result) { @@ -764,6 +1072,20 @@ void c_shuffle(RandomAccessContainer& c, UniformRandomBitGenerator&& gen) { std::forward(gen)); } +// c_sample() +// +// Container-based version of the `std::sample()` function to +// randomly sample elements from the container without replacement using a +// `gen()` uniform random number generator and write them to an iterator range. +template +OutputIterator c_sample(const C& c, OutputIterator result, Distance n, + UniformRandomBitGenerator&& gen) { + return std::sample(container_algorithm_internal::c_begin(c), + container_algorithm_internal::c_end(c), result, n, + std::forward(gen)); +} + //------------------------------------------------------------------------------ // Partition functions //------------------------------------------------------------------------------ @@ -774,7 +1096,7 @@ void c_shuffle(RandomAccessContainer& c, UniformRandomBitGenerator&& gen) { // to test whether all elements in the container for which `pred` returns `true` // precede those for which `pred` is `false`. template -bool c_is_partitioned(const C& c, Pred&& pred) { +constexpr bool c_is_partitioned(const C& c, Pred&& pred) { return std::is_partitioned(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -787,7 +1109,8 @@ bool c_is_partitioned(const C& c, Pred&& pred) { // which `pred` returns `true` precede all those for which it returns `false`, // returning an iterator to the first element of the second group. template -container_algorithm_internal::ContainerIter c_partition(C& c, Pred&& pred) { +constexpr container_algorithm_internal::ContainerIter c_partition( + C& c, Pred&& pred) { return std::partition(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -816,7 +1139,7 @@ container_algorithm_internal::ContainerIter c_stable_partition(C& c, template -std::pair c_partition_copy( +constexpr std::pair c_partition_copy( const C& c, OutputIterator1 out_true, OutputIterator2 out_false, Pred&& pred) { return std::partition_copy(container_algorithm_internal::c_begin(c), @@ -830,8 +1153,8 @@ std::pair c_partition_copy( // to return the first element of an already partitioned container for which // the given `pred` is not `true`. template -container_algorithm_internal::ContainerIter c_partition_point(C& c, - Pred&& pred) { +constexpr container_algorithm_internal::ContainerIter c_partition_point( + C& c, Pred&& pred) { return std::partition_point(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), std::forward(pred)); @@ -846,18 +1169,18 @@ container_algorithm_internal::ContainerIter c_partition_point(C& c, // Container-based version of the `std::sort()` function // to sort elements in ascending order of their values. template -void c_sort(C& c) { +constexpr void c_sort(C& c) { std::sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } // Overload of c_sort() for performing a `comp` comparison other than the // default `operator<`. -template -void c_sort(C& c, Compare&& comp) { +template +constexpr void c_sort(C& c, LessThan&& comp) { std::sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_stable_sort() @@ -873,11 +1196,11 @@ void c_stable_sort(C& c) { // Overload of c_stable_sort() for performing a `comp` comparison other than the // default `operator<`. -template -void c_stable_sort(C& c, Compare&& comp) { +template +void c_stable_sort(C& c, LessThan&& comp) { std::stable_sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_is_sorted() @@ -885,18 +1208,18 @@ void c_stable_sort(C& c, Compare&& comp) { // Container-based version of the `std::is_sorted()` function // to evaluate whether the given container is sorted in ascending order. template -bool c_is_sorted(const C& c) { +constexpr bool c_is_sorted(const C& c) { return std::is_sorted(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } // c_is_sorted() overload for performing a `comp` comparison other than the // default `operator<`. -template -bool c_is_sorted(const C& c, Compare&& comp) { +template +constexpr bool c_is_sorted(const C& c, LessThan&& comp) { return std::is_sorted(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_partial_sort() @@ -905,7 +1228,7 @@ bool c_is_sorted(const C& c, Compare&& comp) { // to rearrange elements within a container such that elements before `middle` // are sorted in ascending order. template -void c_partial_sort( +constexpr void c_partial_sort( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter middle) { std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, @@ -914,24 +1237,25 @@ void c_partial_sort( // Overload of c_partial_sort() for performing a `comp` comparison other than // the default `operator<`. -template -void c_partial_sort( +template +constexpr void c_partial_sort( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter middle, - Compare&& comp) { + LessThan&& comp) { std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_partial_sort_copy() // // Container-based version of the `std::partial_sort_copy()` -// function to sort elements within a container such that elements before -// `middle` are sorted in ascending order, and return the result within an -// iterator. +// function to sort the elements in the given range `result` within the larger +// `sequence` in ascending order (and using `result` as the output parameter). +// At most min(result.last - result.first, sequence.last - sequence.first) +// elements from the sequence will be stored in the result. template -container_algorithm_internal::ContainerIter +constexpr container_algorithm_internal::ContainerIter c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), @@ -941,15 +1265,15 @@ c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { // Overload of c_partial_sort_copy() for performing a `comp` comparison other // than the default `operator<`. -template -container_algorithm_internal::ContainerIter +template +constexpr container_algorithm_internal::ContainerIter c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, - Compare&& comp) { + LessThan&& comp) { return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_begin(result), container_algorithm_internal::c_end(result), - std::forward(comp)); + std::forward(comp)); } // c_is_sorted_until() @@ -958,19 +1282,20 @@ c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, // to return the first element within a container that is not sorted in // ascending order as an iterator. template -container_algorithm_internal::ContainerIter c_is_sorted_until(C& c) { +constexpr container_algorithm_internal::ContainerIter c_is_sorted_until( + C& c) { return std::is_sorted_until(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } // Overload of c_is_sorted_until() for performing a `comp` comparison other than // the default `operator<`. -template -container_algorithm_internal::ContainerIter c_is_sorted_until( - C& c, Compare&& comp) { +template +constexpr container_algorithm_internal::ContainerIter c_is_sorted_until( + C& c, LessThan&& comp) { return std::is_sorted_until(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_nth_element() @@ -981,7 +1306,7 @@ container_algorithm_internal::ContainerIter c_is_sorted_until( // any order, except that all preceding `nth` will be less than that element, // and all following `nth` will be greater than that element. template -void c_nth_element( +constexpr void c_nth_element( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter nth) { std::nth_element(container_algorithm_internal::c_begin(sequence), nth, @@ -990,14 +1315,14 @@ void c_nth_element( // Overload of c_nth_element() for performing a `comp` comparison other than // the default `operator<`. -template -void c_nth_element( +template +constexpr void c_nth_element( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter nth, - Compare&& comp) { + LessThan&& comp) { std::nth_element(container_algorithm_internal::c_begin(sequence), nth, container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1010,21 +1335,20 @@ void c_nth_element( // to return an iterator pointing to the first element in a sorted container // which does not compare less than `value`. template -container_algorithm_internal::ContainerIter c_lower_bound( - Sequence& sequence, T&& value) { +constexpr container_algorithm_internal::ContainerIter c_lower_bound( + Sequence& sequence, const T& value) { return std::lower_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value)); + container_algorithm_internal::c_end(sequence), value); } // Overload of c_lower_bound() for performing a `comp` comparison other than // the default `operator<`. -template -container_algorithm_internal::ContainerIter c_lower_bound( - Sequence& sequence, T&& value, Compare&& comp) { +template +constexpr container_algorithm_internal::ContainerIter c_lower_bound( + Sequence& sequence, const T& value, LessThan&& comp) { return std::lower_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value), std::forward(comp)); + container_algorithm_internal::c_end(sequence), value, + std::forward(comp)); } // c_upper_bound() @@ -1033,21 +1357,20 @@ container_algorithm_internal::ContainerIter c_lower_bound( // to return an iterator pointing to the first element in a sorted container // which is greater than `value`. template -container_algorithm_internal::ContainerIter c_upper_bound( - Sequence& sequence, T&& value) { +constexpr container_algorithm_internal::ContainerIter c_upper_bound( + Sequence& sequence, const T& value) { return std::upper_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value)); + container_algorithm_internal::c_end(sequence), value); } // Overload of c_upper_bound() for performing a `comp` comparison other than // the default `operator<`. -template -container_algorithm_internal::ContainerIter c_upper_bound( - Sequence& sequence, T&& value, Compare&& comp) { +template +constexpr container_algorithm_internal::ContainerIter c_upper_bound( + Sequence& sequence, const T& value, LessThan&& comp) { return std::upper_bound(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value), std::forward(comp)); + container_algorithm_internal::c_end(sequence), value, + std::forward(comp)); } // c_equal_range() @@ -1056,21 +1379,22 @@ container_algorithm_internal::ContainerIter c_upper_bound( // to return an iterator pair pointing to the first and last elements in a // sorted container which compare equal to `value`. template -container_algorithm_internal::ContainerIterPairType -c_equal_range(Sequence& sequence, T&& value) { +constexpr container_algorithm_internal::ContainerIterPairType +c_equal_range(Sequence& sequence, const T& value) { return std::equal_range(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value)); + container_algorithm_internal::c_end(sequence), value); } // Overload of c_equal_range() for performing a `comp` comparison other than // the default `operator<`. -template -container_algorithm_internal::ContainerIterPairType -c_equal_range(Sequence& sequence, T&& value, Compare&& comp) { +template +constexpr container_algorithm_internal::ContainerIterPairType +c_equal_range(Sequence& sequence, const T& value, LessThan&& comp) { return std::equal_range(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value), std::forward(comp)); + container_algorithm_internal::c_end(sequence), value, + std::forward(comp)); } // c_binary_search() @@ -1079,20 +1403,20 @@ c_equal_range(Sequence& sequence, T&& value, Compare&& comp) { // to test if any element in the sorted container contains a value equivalent to // 'value'. template -bool c_binary_search(Sequence&& sequence, T&& value) { +constexpr bool c_binary_search(const Sequence& sequence, const T& value) { return std::binary_search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(value)); + value); } // Overload of c_binary_search() for performing a `comp` comparison other than // the default `operator<`. -template -bool c_binary_search(Sequence&& sequence, T&& value, Compare&& comp) { +template +constexpr bool c_binary_search(const Sequence& sequence, const T& value, + LessThan&& comp) { return std::binary_search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(value), - std::forward(comp)); + value, std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1104,7 +1428,8 @@ bool c_binary_search(Sequence&& sequence, T&& value, Compare&& comp) { // Container-based version of the `std::merge()` function // to merge two sorted containers into a single sorted iterator. template -OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) { +constexpr OutputIterator c_merge(const C1& c1, const C2& c2, + OutputIterator result) { return std::merge(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1113,14 +1438,14 @@ OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) { // Overload of c_merge() for performing a `comp` comparison other than // the default `operator<`. -template -OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result, - Compare&& comp) { +template +constexpr OutputIterator c_merge(const C1& c1, const C2& c2, + OutputIterator result, LessThan&& comp) { return std::merge(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), result, - std::forward(comp)); + std::forward(comp)); } // c_inplace_merge() @@ -1136,13 +1461,13 @@ void c_inplace_merge(C& c, // Overload of c_inplace_merge() for performing a merge using a `comp` other // than `operator<`. -template +template void c_inplace_merge(C& c, container_algorithm_internal::ContainerIter middle, - Compare&& comp) { + LessThan&& comp) { std::inplace_merge(container_algorithm_internal::c_begin(c), middle, container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_includes() @@ -1151,7 +1476,7 @@ void c_inplace_merge(C& c, // to test whether a sorted container `c1` entirely contains another sorted // container `c2`. template -bool c_includes(const C1& c1, const C2& c2) { +constexpr bool c_includes(const C1& c1, const C2& c2) { return std::includes(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1160,13 +1485,13 @@ bool c_includes(const C1& c1, const C2& c2) { // Overload of c_includes() for performing a merge using a `comp` other than // `operator<`. -template -bool c_includes(const C1& c1, const C2& c2, Compare&& comp) { +template +constexpr bool c_includes(const C1& c1, const C2& c2, LessThan&& comp) { return std::includes(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), - std::forward(comp)); + std::forward(comp)); } // c_set_union() @@ -1174,14 +1499,14 @@ bool c_includes(const C1& c1, const C2& c2, Compare&& comp) { // Container-based version of the `std::set_union()` function // to return an iterator containing the union of two containers; duplicate // values are not copied into the output. -template ::value, - void>::type, - typename = typename std::enable_if< - !container_algorithm_internal::IsUnorderedContainer::value, - void>::type> -OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { +template < + typename C1, typename C2, typename OutputIterator, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>> +constexpr OutputIterator c_set_union(const C1& c1, const C2& c2, + OutputIterator output) { return std::set_union(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1190,35 +1515,38 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { // Overload of c_set_union() for performing a merge using a `comp` other than // `operator<`. -template ::value, - void>::type, - typename = typename std::enable_if< - !container_algorithm_internal::IsUnorderedContainer::value, - void>::type> -OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, - Compare&& comp) { +template < + typename C1, typename C2, typename OutputIterator, typename LessThan, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>> +constexpr OutputIterator c_set_union(const C1& c1, const C2& c2, + OutputIterator output, LessThan&& comp) { return std::set_union(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } // c_set_intersection() // // Container-based version of the `std::set_intersection()` function -// to return an iterator containing the intersection of two containers. -template ::value, - void>::type, - typename = typename std::enable_if< - !container_algorithm_internal::IsUnorderedContainer::value, - void>::type> -OutputIterator c_set_intersection(const C1& c1, const C2& c2, - OutputIterator output) { +// to return an iterator containing the intersection of two sorted containers. +template < + typename C1, typename C2, typename OutputIterator, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>> +constexpr OutputIterator c_set_intersection(const C1& c1, const C2& c2, + OutputIterator output) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using operator<. + ABSL_ASSERT(absl::c_is_sorted(c1)); + ABSL_ASSERT(absl::c_is_sorted(c2)); return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1227,20 +1555,25 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2, // Overload of c_set_intersection() for performing a merge using a `comp` other // than `operator<`. -template ::value, - void>::type, - typename = typename std::enable_if< - !container_algorithm_internal::IsUnorderedContainer::value, - void>::type> -OutputIterator c_set_intersection(const C1& c1, const C2& c2, - OutputIterator output, Compare&& comp) { +template < + typename C1, typename C2, typename OutputIterator, typename LessThan, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>> +constexpr OutputIterator c_set_intersection(const C1& c1, const C2& c2, + OutputIterator output, + LessThan&& comp) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using the same comparator. + ABSL_ASSERT(absl::c_is_sorted(c1, comp)); + ABSL_ASSERT(absl::c_is_sorted(c2, comp)); return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } // c_set_difference() @@ -1248,15 +1581,14 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2, // Container-based version of the `std::set_difference()` function // to return an iterator containing elements present in the first container but // not in the second. -template ::value, - void>::type, - typename = typename std::enable_if< - !container_algorithm_internal::IsUnorderedContainer::value, - void>::type> -OutputIterator c_set_difference(const C1& c1, const C2& c2, - OutputIterator output) { +template < + typename C1, typename C2, typename OutputIterator, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>> +constexpr OutputIterator c_set_difference(const C1& c1, const C2& c2, + OutputIterator output) { return std::set_difference(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1265,20 +1597,20 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2, // Overload of c_set_difference() for performing a merge using a `comp` other // than `operator<`. -template ::value, - void>::type, - typename = typename std::enable_if< - !container_algorithm_internal::IsUnorderedContainer::value, - void>::type> -OutputIterator c_set_difference(const C1& c1, const C2& c2, - OutputIterator output, Compare&& comp) { +template < + typename C1, typename C2, typename OutputIterator, typename LessThan, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>> +constexpr OutputIterator c_set_difference(const C1& c1, const C2& c2, + OutputIterator output, + LessThan&& comp) { return std::set_difference(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } // c_set_symmetric_difference() @@ -1286,15 +1618,14 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2, // Container-based version of the `std::set_symmetric_difference()` // function to return an iterator containing elements present in either one // container or the other, but not both. -template ::value, - void>::type, - typename = typename std::enable_if< - !container_algorithm_internal::IsUnorderedContainer::value, - void>::type> -OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, - OutputIterator output) { +template < + typename C1, typename C2, typename OutputIterator, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>> +constexpr OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, + OutputIterator output) { return std::set_symmetric_difference( container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), @@ -1304,22 +1635,21 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, // Overload of c_set_symmetric_difference() for performing a merge using a // `comp` other than `operator<`. -template ::value, - void>::type, - typename = typename std::enable_if< - !container_algorithm_internal::IsUnorderedContainer::value, - void>::type> -OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, - OutputIterator output, - Compare&& comp) { +template < + typename C1, typename C2, typename OutputIterator, typename LessThan, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>, + typename = std::enable_if_t< + !container_algorithm_internal::IsUnorderedContainer::value, void>> +constexpr OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, + OutputIterator output, + LessThan&& comp) { return std::set_symmetric_difference( container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1331,18 +1661,18 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, // Container-based version of the `std::push_heap()` function // to push a value onto a container heap. template -void c_push_heap(RandomAccessContainer& sequence) { +constexpr void c_push_heap(RandomAccessContainer& sequence) { std::push_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } // Overload of c_push_heap() for performing a push operation on a heap using a // `comp` other than `operator<`. -template -void c_push_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +constexpr void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::push_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_pop_heap() @@ -1350,18 +1680,18 @@ void c_push_heap(RandomAccessContainer& sequence, Compare&& comp) { // Container-based version of the `std::pop_heap()` function // to pop a value from a heap container. template -void c_pop_heap(RandomAccessContainer& sequence) { +constexpr void c_pop_heap(RandomAccessContainer& sequence) { std::pop_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } // Overload of c_pop_heap() for performing a pop operation on a heap using a // `comp` other than `operator<`. -template -void c_pop_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +constexpr void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::pop_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_make_heap() @@ -1369,18 +1699,18 @@ void c_pop_heap(RandomAccessContainer& sequence, Compare&& comp) { // Container-based version of the `std::make_heap()` function // to make a container a heap. template -void c_make_heap(RandomAccessContainer& sequence) { +constexpr void c_make_heap(RandomAccessContainer& sequence) { std::make_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } // Overload of c_make_heap() for performing heap comparisons using a // `comp` other than `operator<` -template -void c_make_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +constexpr void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::make_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_sort_heap() @@ -1388,18 +1718,18 @@ void c_make_heap(RandomAccessContainer& sequence, Compare&& comp) { // Container-based version of the `std::sort_heap()` function // to sort a heap into ascending order (after which it is no longer a heap). template -void c_sort_heap(RandomAccessContainer& sequence) { +constexpr void c_sort_heap(RandomAccessContainer& sequence) { std::sort_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } // Overload of c_sort_heap() for performing heap comparisons using a // `comp` other than `operator<` -template -void c_sort_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +constexpr void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::sort_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_is_heap() @@ -1407,18 +1737,19 @@ void c_sort_heap(RandomAccessContainer& sequence, Compare&& comp) { // Container-based version of the `std::is_heap()` function // to check whether the given container is a heap. template -bool c_is_heap(const RandomAccessContainer& sequence) { +constexpr bool c_is_heap(const RandomAccessContainer& sequence) { return std::is_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); } // Overload of c_is_heap() for performing heap comparisons using a // `comp` other than `operator<` -template -bool c_is_heap(const RandomAccessContainer& sequence, Compare&& comp) { +template +constexpr bool c_is_heap(const RandomAccessContainer& sequence, + LessThan&& comp) { return std::is_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_is_heap_until() @@ -1426,7 +1757,7 @@ bool c_is_heap(const RandomAccessContainer& sequence, Compare&& comp) { // Container-based version of the `std::is_heap_until()` function // to find the first element in a given container which is not in heap order. template -container_algorithm_internal::ContainerIter +constexpr container_algorithm_internal::ContainerIter c_is_heap_until(RandomAccessContainer& sequence) { return std::is_heap_until(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); @@ -1434,12 +1765,12 @@ c_is_heap_until(RandomAccessContainer& sequence) { // Overload of c_is_heap_until() for performing heap comparisons using a // `comp` other than `operator<` -template -container_algorithm_internal::ContainerIter -c_is_heap_until(RandomAccessContainer& sequence, Compare&& comp) { +template +constexpr container_algorithm_internal::ContainerIter +c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) { return std::is_heap_until(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1452,7 +1783,7 @@ c_is_heap_until(RandomAccessContainer& sequence, Compare&& comp) { // to return an iterator pointing to the element with the smallest value, using // `operator<` to make the comparisons. template -container_algorithm_internal::ContainerIter c_min_element( +constexpr container_algorithm_internal::ContainerIter c_min_element( Sequence& sequence) { return std::min_element(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); @@ -1460,12 +1791,12 @@ container_algorithm_internal::ContainerIter c_min_element( // Overload of c_min_element() for performing a `comp` comparison other than // `operator<`. -template -container_algorithm_internal::ContainerIter c_min_element( - Sequence& sequence, Compare&& comp) { +template +constexpr container_algorithm_internal::ContainerIter c_min_element( + Sequence& sequence, LessThan&& comp) { return std::min_element(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_max_element() @@ -1474,7 +1805,7 @@ container_algorithm_internal::ContainerIter c_min_element( // to return an iterator pointing to the element with the largest value, using // `operator<` to make the comparisons. template -container_algorithm_internal::ContainerIter c_max_element( +constexpr container_algorithm_internal::ContainerIter c_max_element( Sequence& sequence) { return std::max_element(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence)); @@ -1482,12 +1813,12 @@ container_algorithm_internal::ContainerIter c_max_element( // Overload of c_max_element() for performing a `comp` comparison other than // `operator<`. -template -container_algorithm_internal::ContainerIter c_max_element( - Sequence& sequence, Compare&& comp) { +template +constexpr container_algorithm_internal::ContainerIter c_max_element( + Sequence& sequence, LessThan&& comp) { return std::max_element(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_minmax_element() @@ -1497,7 +1828,7 @@ container_algorithm_internal::ContainerIter c_max_element( // smallest and largest values, respectively, using `operator<` to make the // comparisons. template -container_algorithm_internal::ContainerIterPairType +constexpr container_algorithm_internal::ContainerIterPairType c_minmax_element(C& c) { return std::minmax_element(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); @@ -1505,12 +1836,12 @@ c_minmax_element(C& c) { // Overload of c_minmax_element() for performing `comp` comparisons other than // `operator<`. -template -container_algorithm_internal::ContainerIterPairType -c_minmax_element(C& c, Compare&& comp) { +template +constexpr container_algorithm_internal::ContainerIterPairType +c_minmax_element(C& c, LessThan&& comp) { return std::minmax_element(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1525,7 +1856,8 @@ c_minmax_element(C& c, Compare&& comp) { // that capital letters ("A-Z") have ASCII values less than lowercase letters // ("a-z"). template -bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) { +constexpr bool c_lexicographical_compare(const Sequence1& sequence1, + const Sequence2& sequence2) { return std::lexicographical_compare( container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_end(sequence1), @@ -1535,15 +1867,16 @@ bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) { // Overload of c_lexicographical_compare() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template -bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2, - Compare&& comp) { +template +constexpr bool c_lexicographical_compare(const Sequence1& sequence1, + const Sequence2& sequence2, + LessThan&& comp) { return std::lexicographical_compare( container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_end(sequence1), container_algorithm_internal::c_begin(sequence2), container_algorithm_internal::c_end(sequence2), - std::forward(comp)); + std::forward(comp)); } // c_next_permutation() @@ -1552,18 +1885,18 @@ bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2, // to rearrange a container's elements into the next lexicographically greater // permutation. template -bool c_next_permutation(C& c) { +constexpr bool c_next_permutation(C& c) { return std::next_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } // Overload of c_next_permutation() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template -bool c_next_permutation(C& c, Compare&& comp) { +template +constexpr bool c_next_permutation(C& c, LessThan&& comp) { return std::next_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_prev_permutation() @@ -1572,18 +1905,18 @@ bool c_next_permutation(C& c, Compare&& comp) { // to rearrange a container's elements into the next lexicographically lesser // permutation. template -bool c_prev_permutation(C& c) { +constexpr bool c_prev_permutation(C& c) { return std::prev_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c)); } // Overload of c_prev_permutation() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template -bool c_prev_permutation(C& c, Compare&& comp) { +template +constexpr bool c_prev_permutation(C& c, LessThan&& comp) { return std::prev_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1592,26 +1925,26 @@ bool c_prev_permutation(C& c, Compare&& comp) { // c_iota() // -// Container-based version of the `std::iota()` function +// Container-based version of the `std::iota()` function // to compute successive values of `value`, as if incremented with `++value` -// after each element is written. and write them to the container. +// after each element is written, and write them to the container. template -void c_iota(Sequence& sequence, T&& value) { +constexpr void c_iota(Sequence& sequence, const T& value) { std::iota(container_algorithm_internal::c_begin(sequence), - container_algorithm_internal::c_end(sequence), - std::forward(value)); + container_algorithm_internal::c_end(sequence), value); } + // c_accumulate() // -// Container-based version of the `std::accumulate()` function +// Container-based version of the `std::accumulate()` function // to accumulate the element values of a container to `init` and return that // accumulation by value. // // Note: Due to a language technicality this function has return type -// absl::decay_t. As a user of this function you can casually read +// std::decay_t. As a user of this function you can casually read // this as "returns T by value" and assume it does the right thing. template -decay_t c_accumulate(const Sequence& sequence, T&& init) { +constexpr std::decay_t c_accumulate(const Sequence& sequence, T&& init) { return std::accumulate(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(init)); @@ -1620,8 +1953,8 @@ decay_t c_accumulate(const Sequence& sequence, T&& init) { // Overload of c_accumulate() for using a binary operations other than // addition for computing the accumulation. template -decay_t c_accumulate(const Sequence& sequence, T&& init, - BinaryOp&& binary_op) { +constexpr std::decay_t c_accumulate(const Sequence& sequence, T&& init, + BinaryOp&& binary_op) { return std::accumulate(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(init), @@ -1630,15 +1963,15 @@ decay_t c_accumulate(const Sequence& sequence, T&& init, // c_inner_product() // -// Container-based version of the `std::inner_product()` function +// Container-based version of the `std::inner_product()` function // to compute the cumulative inner product of container element pairs. // // Note: Due to a language technicality this function has return type -// absl::decay_t. As a user of this function you can casually read +// std::decay_t. As a user of this function you can casually read // this as "returns T by value" and assume it does the right thing. template -decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, - T&& sum) { +constexpr std::decay_t c_inner_product(const Sequence1& factors1, + const Sequence2& factors2, T&& sum) { return std::inner_product(container_algorithm_internal::c_begin(factors1), container_algorithm_internal::c_end(factors1), container_algorithm_internal::c_begin(factors2), @@ -1650,8 +1983,9 @@ decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, // the product between the two container's element pair). template -decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, - T&& sum, BinaryOp1&& op1, BinaryOp2&& op2) { +constexpr std::decay_t c_inner_product(const Sequence1& factors1, + const Sequence2& factors2, T&& sum, + BinaryOp1&& op1, BinaryOp2&& op2) { return std::inner_product(container_algorithm_internal::c_begin(factors1), container_algorithm_internal::c_end(factors1), container_algorithm_internal::c_begin(factors2), @@ -1661,12 +1995,12 @@ decay_t c_inner_product(const Sequence1& factors1, const Sequence2& factors2, // c_adjacent_difference() // -// Container-based version of the `std::adjacent_difference()` +// Container-based version of the `std::adjacent_difference()` // function to compute the difference between each element and the one preceding // it and write it to an iterator. template -OutputIt c_adjacent_difference(const InputSequence& input, - OutputIt output_first) { +constexpr OutputIt c_adjacent_difference(const InputSequence& input, + OutputIt output_first) { return std::adjacent_difference(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output_first); @@ -1675,8 +2009,8 @@ OutputIt c_adjacent_difference(const InputSequence& input, // Overload of c_adjacent_difference() for using a binary operation other than // subtraction to compute the adjacent difference. template -OutputIt c_adjacent_difference(const InputSequence& input, - OutputIt output_first, BinaryOp&& op) { +constexpr OutputIt c_adjacent_difference(const InputSequence& input, + OutputIt output_first, BinaryOp&& op) { return std::adjacent_difference(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output_first, std::forward(op)); @@ -1684,12 +2018,13 @@ OutputIt c_adjacent_difference(const InputSequence& input, // c_partial_sum() // -// Container-based version of the `std::partial_sum()` function +// Container-based version of the `std::partial_sum()` function // to compute the partial sum of the elements in a sequence and write them // to an iterator. The partial sum is the sum of all element values so far in // the sequence. template -OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first) { +constexpr OutputIt c_partial_sum(const InputSequence& input, + OutputIt output_first) { return std::partial_sum(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output_first); @@ -1698,13 +2033,14 @@ OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first) { // Overload of c_partial_sum() for using a binary operation other than addition // to compute the "partial sum". template -OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first, - BinaryOp&& op) { +constexpr OutputIt c_partial_sum(const InputSequence& input, + OutputIt output_first, BinaryOp&& op) { return std::partial_sum(container_algorithm_internal::c_begin(input), container_algorithm_internal::c_end(input), output_first, std::forward(op)); } +ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_ALGORITHM_CONTAINER_H_ diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc index 86bf9d3ea32..e0253827a32 100644 --- a/absl/algorithm/container_test.cc +++ b/absl/algorithm/container_test.cc @@ -14,6 +14,10 @@ #include "absl/algorithm/container.h" +#include +#include +#include +#include #include #include #include @@ -22,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -30,18 +35,27 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/casts.h" +#include "absl/base/config.h" #include "absl/base/macros.h" #include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/random/random.h" #include "absl/types/span.h" +#ifdef __cpp_lib_span +#include // NOLINT(build/c++20) +#endif + namespace { using ::testing::Each; using ::testing::ElementsAre; using ::testing::Gt; using ::testing::IsNull; +using ::testing::IsSubsetOf; using ::testing::Lt; using ::testing::Pointee; +using ::testing::SizeIs; using ::testing::Truly; using ::testing::UnorderedElementsAre; @@ -57,9 +71,7 @@ class NonMutatingTest : public testing::Test { }; struct AccumulateCalls { - void operator()(int value) { - calls.push_back(value); - } + void operator()(int value) { calls.push_back(value); } std::vector calls; }; @@ -68,15 +80,41 @@ bool BinPredicate(int v1, int v2) { return v1 < v2; } bool Equals(int v1, int v2) { return v1 == v2; } bool IsOdd(int x) { return x % 2 != 0; } +TEST(Span, IsSpan) { + static_assert( + absl::container_algorithm_internal::IsSpan>::value); + static_assert( + absl::container_algorithm_internal::IsSpan>::value); + static_assert(absl::container_algorithm_internal::IsSpan< + volatile absl::Span>::value); + static_assert(absl::container_algorithm_internal::IsSpan< + const volatile absl::Span>::value); + + static_assert( + !absl::container_algorithm_internal::IsSpan&>::value); + static_assert( + !absl::container_algorithm_internal::IsSpan&&>::value); + +#ifdef __cpp_lib_span + static_assert( + absl::container_algorithm_internal::IsSpan>::value); + static_assert( + !absl::container_algorithm_internal::IsSpan&>::value); + static_assert( + !absl::container_algorithm_internal::IsSpan&&>::value); +#endif +} TEST_F(NonMutatingTest, Distance) { - EXPECT_EQ(container_.size(), absl::c_distance(container_)); - EXPECT_EQ(sequence_.size(), absl::c_distance(sequence_)); - EXPECT_EQ(vector_.size(), absl::c_distance(vector_)); - EXPECT_EQ(ABSL_ARRAYSIZE(array_), absl::c_distance(array_)); + EXPECT_EQ(container_.size(), + static_cast(absl::c_distance(container_))); + EXPECT_EQ(sequence_.size(), static_cast(absl::c_distance(sequence_))); + EXPECT_EQ(vector_.size(), static_cast(absl::c_distance(vector_))); + EXPECT_EQ(std::size(array_), static_cast(absl::c_distance(array_))); // Works with a temporary argument. - EXPECT_EQ(vector_.size(), absl::c_distance(std::vector(vector_))); + EXPECT_EQ(vector_.size(), + static_cast(absl::c_distance(std::vector(vector_)))); } TEST_F(NonMutatingTest, Distance_OverloadedBeginEnd) { @@ -110,6 +148,11 @@ TEST_F(NonMutatingTest, FindReturnsCorrectType) { absl::c_find(absl::implicit_cast&>(sequence_), 3); } +TEST_F(NonMutatingTest, Contains) { + EXPECT_TRUE(absl::c_contains(container_, 3)); + EXPECT_FALSE(absl::c_contains(container_, 4)); +} + TEST_F(NonMutatingTest, FindIf) { absl::c_find_if(container_, Predicate); } TEST_F(NonMutatingTest, FindIfNot) { @@ -129,11 +172,13 @@ TEST_F(NonMutatingTest, FindEndWithPredicate) { TEST_F(NonMutatingTest, FindFirstOf) { absl::c_find_first_of(container_, sequence_); absl::c_find_first_of(sequence_, container_); + absl::c_find_first_of(sequence_, std::array{1, 2}); } TEST_F(NonMutatingTest, FindFirstOfWithPredicate) { absl::c_find_first_of(container_, sequence_, BinPredicate); absl::c_find_first_of(sequence_, container_, BinPredicate); + absl::c_find_first_of(sequence_, std::array{1, 2}, BinPredicate); } TEST_F(NonMutatingTest, AdjacentFind) { absl::c_adjacent_find(sequence_); } @@ -151,35 +196,118 @@ TEST_F(NonMutatingTest, CountIf) { } TEST_F(NonMutatingTest, Mismatch) { - absl::c_mismatch(container_, sequence_); - absl::c_mismatch(sequence_, container_); + // Testing necessary as absl::c_mismatch executes logic. + { + auto result = absl::c_mismatch(vector_, sequence_); + EXPECT_EQ(result.first, vector_.end()); + EXPECT_EQ(result.second, sequence_.end()); + } + { + auto result = absl::c_mismatch(sequence_, vector_); + EXPECT_EQ(result.first, sequence_.end()); + EXPECT_EQ(result.second, vector_.end()); + } + + sequence_.back() = 5; + { + auto result = absl::c_mismatch(vector_, sequence_); + EXPECT_EQ(result.first, std::prev(vector_.end())); + EXPECT_EQ(result.second, std::prev(sequence_.end())); + } + { + auto result = absl::c_mismatch(sequence_, vector_); + EXPECT_EQ(result.first, std::prev(sequence_.end())); + EXPECT_EQ(result.second, std::prev(vector_.end())); + } + + sequence_.pop_back(); + { + auto result = absl::c_mismatch(vector_, sequence_); + EXPECT_EQ(result.first, std::prev(vector_.end())); + EXPECT_EQ(result.second, sequence_.end()); + } + { + auto result = absl::c_mismatch(sequence_, vector_); + EXPECT_EQ(result.first, sequence_.end()); + EXPECT_EQ(result.second, std::prev(vector_.end())); + } + { + struct NoNotEquals { + constexpr bool operator==(NoNotEquals) const { return true; } + constexpr bool operator!=(NoNotEquals) const = delete; + }; + std::vector first; + std::list second; + + // Check this still compiles. + absl::c_mismatch(first, second); + } } TEST_F(NonMutatingTest, MismatchWithPredicate) { - absl::c_mismatch(container_, sequence_, BinPredicate); - absl::c_mismatch(sequence_, container_, BinPredicate); + // Testing necessary as absl::c_mismatch executes logic. + { + auto result = absl::c_mismatch(vector_, sequence_, BinPredicate); + EXPECT_EQ(result.first, vector_.begin()); + EXPECT_EQ(result.second, sequence_.begin()); + } + { + auto result = absl::c_mismatch(sequence_, vector_, BinPredicate); + EXPECT_EQ(result.first, sequence_.begin()); + EXPECT_EQ(result.second, vector_.begin()); + } + + sequence_.front() = 0; + { + auto result = absl::c_mismatch(vector_, sequence_, BinPredicate); + EXPECT_EQ(result.first, vector_.begin()); + EXPECT_EQ(result.second, sequence_.begin()); + } + { + auto result = absl::c_mismatch(sequence_, vector_, BinPredicate); + EXPECT_EQ(result.first, std::next(sequence_.begin())); + EXPECT_EQ(result.second, std::next(vector_.begin())); + } + + sequence_.clear(); + { + auto result = absl::c_mismatch(vector_, sequence_, BinPredicate); + EXPECT_EQ(result.first, vector_.begin()); + EXPECT_EQ(result.second, sequence_.end()); + } + { + auto result = absl::c_mismatch(sequence_, vector_, BinPredicate); + EXPECT_EQ(result.first, sequence_.end()); + EXPECT_EQ(result.second, vector_.begin()); + } } TEST_F(NonMutatingTest, Equal) { EXPECT_TRUE(absl::c_equal(vector_, sequence_)); EXPECT_TRUE(absl::c_equal(sequence_, vector_)); + EXPECT_TRUE(absl::c_equal(sequence_, array_)); + EXPECT_TRUE(absl::c_equal(array_, vector_)); // Test that behavior appropriately differs from that of equal(). std::vector vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(absl::c_equal(vector_plus, sequence_)); EXPECT_FALSE(absl::c_equal(sequence_, vector_plus)); + EXPECT_FALSE(absl::c_equal(array_, vector_plus)); } TEST_F(NonMutatingTest, EqualWithPredicate) { EXPECT_TRUE(absl::c_equal(vector_, sequence_, Equals)); EXPECT_TRUE(absl::c_equal(sequence_, vector_, Equals)); + EXPECT_TRUE(absl::c_equal(array_, sequence_, Equals)); + EXPECT_TRUE(absl::c_equal(vector_, array_, Equals)); // Test that behavior appropriately differs from that of equal(). std::vector vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(absl::c_equal(vector_plus, sequence_, Equals)); EXPECT_FALSE(absl::c_equal(sequence_, vector_plus, Equals)); + EXPECT_FALSE(absl::c_equal(vector_plus, array_, Equals)); } TEST_F(NonMutatingTest, IsPermutation) { @@ -219,6 +347,17 @@ TEST_F(NonMutatingTest, SearchWithPredicate) { absl::c_search(vector_, sequence_, BinPredicate); } +TEST_F(NonMutatingTest, ContainsSubrange) { + EXPECT_TRUE(absl::c_contains_subrange(sequence_, vector_)); + EXPECT_TRUE(absl::c_contains_subrange(vector_, sequence_)); + EXPECT_TRUE(absl::c_contains_subrange(array_, sequence_)); +} + +TEST_F(NonMutatingTest, ContainsSubrangeWithPredicate) { + EXPECT_TRUE(absl::c_contains_subrange(sequence_, vector_, Equals)); + EXPECT_TRUE(absl::c_contains_subrange(vector_, sequence_, Equals)); +} + TEST_F(NonMutatingTest, SearchN) { absl::c_search_n(sequence_, 3, 1); } TEST_F(NonMutatingTest, SearchNWithPredicate) { @@ -513,11 +652,9 @@ TEST_F(SortingTest, IsSortedUntil) { TEST_F(SortingTest, NthElement) { std::vector unsorted = {2, 4, 1, 3}; absl::c_nth_element(unsorted, unsorted.begin() + 2); - EXPECT_THAT(unsorted, - ElementsAre(Lt(3), Lt(3), 3, Gt(3))); + EXPECT_THAT(unsorted, ElementsAre(Lt(3), Lt(3), 3, Gt(3))); absl::c_nth_element(unsorted, unsorted.begin() + 2, std::greater()); - EXPECT_THAT(unsorted, - ElementsAre(Gt(2), Gt(2), 2, Lt(2))); + EXPECT_THAT(unsorted, ElementsAre(Gt(2), Gt(2), 2, Lt(2))); } TEST(MutatingTest, IsPartitioned) { @@ -606,6 +743,222 @@ TEST(MutatingTest, CopyN) { EXPECT_EQ(expected, actual); } +TEST(MutatingTest, CopyNWithNegativeN) { +#ifdef _LIBCPP_VERSION + GTEST_SKIP() << "libc++ does not handle negative counts correctly"; +#else + const std::vector input = {1, 2, 3}; + std::vector actual = {0, 0, 0}; + absl::c_copy_n(input, -1, actual.begin()); + EXPECT_THAT(actual, ElementsAre(0, 0, 0)); +#endif +} + +TEST(MutatingTest, CopyToContainer) { + const std::vector input = {1, 2, 3}; + std::vector actual = {0, 0, 0, 4, 5}; + absl::c_copy(input, actual); + EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5)); +} + +TEST(MutatingTest, CopyToSpanRvalue) { + const std::vector input = {1, 2, 3}; + std::vector actual = {0, 0, 0, 4, 5}; + absl::c_copy(input, absl::MakeSpan(actual)); + EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5)); +} + +TEST(MutatingTest, CopyNToContainer) { + const std::vector input = {1, 2, 3, 4, 5}; + std::vector actual = {0, 0, 0, 0, 0}; + absl::c_copy_n(input, 2, actual); + EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); +} + +TEST(MutatingTest, CopyNToSpanRvalue) { + const std::vector input = {1, 2, 3, 4, 5}; + std::vector actual = {0, 0, 0, 0, 0}; + absl::c_copy_n(input, 2, absl::MakeSpan(actual)); + EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); +} + +TEST(MutatingTest, CopyNToContainerWithZeroN) { + const std::vector input = {1, 2, 3, 4, 5}; + std::vector actual = {0, 0, 0, 0, 0}; + absl::c_copy_n(input, 0, actual); + EXPECT_THAT(actual, ElementsAre(0, 0, 0, 0, 0)); +} + +TEST(MutatingTest, CopyNToContainerWithNegativeN) { +#ifdef _LIBCPP_VERSION + GTEST_SKIP() << "libc++ does not handle negative counts correctly"; +#else + const std::vector input = {1, 2, 3}; + std::vector actual = {0, 0, 0}; + absl::c_copy_n(input, -1, actual); + EXPECT_THAT(actual, ElementsAre(0, 0, 0)); +#endif +} + +TEST(MutatingTest, CopyToDifferentContainerType) { + const std::list input = {1, 2, 3}; + std::array actual = {0, 0, 0, 4, 5}; + absl::c_copy(input, actual); + EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5)); +} + +TEST(MutatingTest, CopyNToDifferentContainerType) { + const std::list input = {1, 2, 3, 4, 5}; + std::array actual = {0, 0, 0, 0, 0}; + absl::c_copy_n(input, 2, actual); + EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); +} + +TEST(MutatingTest, CopyToCArray) { + const std::vector input = {1, 2, 3}; + int actual[5] = {0, 0, 0, 4, 5}; + absl::c_copy(input, actual); + EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5)); +} + +TEST(MutatingTest, CopyNToCArray) { + const std::vector input = {1, 2, 3, 4, 5}; + int actual[5] = {0, 0, 0, 0, 0}; + absl::c_copy_n(input, 2, actual); + EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); +} + +TEST(MutatingTest, CopyFromCArray) { + const int input[5] = {1, 2, 3, 4, 5}; + std::vector actual = {0, 0, 0, 0, 0}; + absl::c_copy(input, actual); + EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5)); +} + +TEST(MutatingTest, CopyNFromCArray) { + const int input[5] = {1, 2, 3, 4, 5}; + std::vector actual = {0, 0, 0, 0, 0}; + absl::c_copy_n(input, 2, actual); + EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); +} + +TEST(MutatingTest, CopyContainerWithNoSizeMethod) { + const std::forward_list input = {1, 2, 3}; + std::forward_list actual = {0, 0, 0, 4, 5}; + absl::c_copy(input, actual); + EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5)); +} + +TEST(MutatingTest, CopyNContainerWithNoSizeMethod) { + const std::forward_list input = {1, 2, 3, 4, 5}; + std::forward_list actual = {0, 0, 0, 0, 0}; + absl::c_copy_n(input, 2, actual); + EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); +} + + +bool IsHardened() { + bool hardened = false; + ABSL_HARDENING_ASSERT([&hardened]() { + hardened = true; + return true; + }()); + return hardened; +} + +TEST(MutatingTest, CopyToCArrayInvalidSize) { + const std::vector input = {1, 2, 3}; + int actual[2] = {0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_copy(input, actual), ""); + } +#endif +} + +TEST(MutatingTest, CopyNToCArrayInvalidSize) { + const std::vector input = {1, 2, 3}; + int actual[2] = {0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_copy_n(input, 3, actual), ""); + } +#endif +} + +TEST(MutatingTest, CopyNToCArrayNGreaterThanInput) { + const std::vector input = {1, 2, 3}; + int actual[4] = {0, 0, 0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_copy_n(input, 4, actual), ""); + } +#endif +} + +TEST(MutatingTest, CopyToContainerInvalidSize) { + const std::list input = {1, 2, 3, 4, 5}; + std::list actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_copy(input, actual), ""); + } +#endif +} + +TEST(MutatingTest, CopyNToContainerNGreaterThanInput) { + const std::vector input = {1, 2, 3}; + std::vector actual = {0, 0, 0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_copy_n(input, 4, actual), ""); + } +#endif +} + +TEST(MutatingTest, CopyNToContainerNGreaterThanOutput) { + const std::vector input = {1, 2, 3}; + std::vector actual = {0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_copy_n(input, 3, actual), ""); + } +#endif +} + +TEST(MutatingTest, CopyToForwardListInvalidSize) { + const std::forward_list input = {1, 2, 3, 4, 5}; + std::forward_list actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_copy(input, actual), ""); + } +#endif +} + +TEST(MutatingTest, CopyNToForwardListNGreaterThanInput) { + const std::forward_list input = {1, 2, 3}; + std::forward_list actual = {0, 0, 0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_copy_n(input, 4, actual), ""); + } +#endif +} + +TEST(MutatingTest, CopyNToForwardListNGreaterThanOutput) { + const std::forward_list input = {1, 2, 3}; + std::forward_list actual = {0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_copy_n(input, 3, actual), ""); + } +#endif +} + TEST(MutatingTest, CopyIf) { const std::list input = {1, 2, 3}; std::vector output; @@ -623,11 +976,11 @@ TEST(MutatingTest, CopyBackward) { TEST(MutatingTest, Move) { std::vector> src; - src.emplace_back(absl::make_unique(1)); - src.emplace_back(absl::make_unique(2)); - src.emplace_back(absl::make_unique(3)); - src.emplace_back(absl::make_unique(4)); - src.emplace_back(absl::make_unique(5)); + src.emplace_back(std::make_unique(1)); + src.emplace_back(std::make_unique(2)); + src.emplace_back(std::make_unique(3)); + src.emplace_back(std::make_unique(4)); + src.emplace_back(std::make_unique(5)); std::vector> dest = {}; absl::c_move(src, std::back_inserter(dest)); @@ -638,11 +991,11 @@ TEST(MutatingTest, Move) { TEST(MutatingTest, MoveBackward) { std::vector> actual; - actual.emplace_back(absl::make_unique(1)); - actual.emplace_back(absl::make_unique(2)); - actual.emplace_back(absl::make_unique(3)); - actual.emplace_back(absl::make_unique(4)); - actual.emplace_back(absl::make_unique(5)); + actual.emplace_back(std::make_unique(1)); + actual.emplace_back(std::make_unique(2)); + actual.emplace_back(std::make_unique(3)); + actual.emplace_back(std::make_unique(4)); + actual.emplace_back(std::make_unique(5)); auto subrange = absl::MakeSpan(actual.data(), 3); absl::c_move_backward(subrange, actual.end()); EXPECT_THAT(actual, ElementsAre(IsNull(), IsNull(), Pointee(1), Pointee(2), @@ -652,9 +1005,9 @@ TEST(MutatingTest, MoveBackward) { TEST(MutatingTest, MoveWithRvalue) { auto MakeRValueSrc = [] { std::vector> src; - src.emplace_back(absl::make_unique(1)); - src.emplace_back(absl::make_unique(2)); - src.emplace_back(absl::make_unique(3)); + src.emplace_back(std::make_unique(1)); + src.emplace_back(std::make_unique(2)); + src.emplace_back(std::make_unique(3)); return src; }; @@ -664,12 +1017,136 @@ TEST(MutatingTest, MoveWithRvalue) { Pointee(2), Pointee(3))); } +TEST(MutatingTest, MoveToContainer) { + std::vector> input; + input.push_back(std::make_unique(1)); + input.push_back(std::make_unique(2)); + input.push_back(std::make_unique(3)); + + std::vector> actual(5); + absl::c_move(input, actual); + + EXPECT_EQ(input[0], nullptr); + EXPECT_EQ(input[1], nullptr); + EXPECT_EQ(input[2], nullptr); + + ASSERT_NE(actual[0], nullptr); + EXPECT_EQ(*actual[0], 1); + ASSERT_NE(actual[1], nullptr); + EXPECT_EQ(*actual[1], 2); + ASSERT_NE(actual[2], nullptr); + EXPECT_EQ(*actual[2], 3); + EXPECT_EQ(actual[3], nullptr); +} + +TEST(MutatingTest, MoveToSpanRvalue) { + std::vector> input; + input.push_back(std::make_unique(1)); + input.push_back(std::make_unique(2)); + input.push_back(std::make_unique(3)); + + std::vector> actual(5); + absl::c_move(input, absl::MakeSpan(actual)); + + EXPECT_EQ(input[0], nullptr); + EXPECT_EQ(input[1], nullptr); + EXPECT_EQ(input[2], nullptr); + + ASSERT_NE(actual[0], nullptr); + EXPECT_EQ(*actual[0], 1); + ASSERT_NE(actual[1], nullptr); + EXPECT_EQ(*actual[1], 2); + ASSERT_NE(actual[2], nullptr); + EXPECT_EQ(*actual[2], 3); + EXPECT_EQ(actual[3], nullptr); +} + +TEST(MutatingTest, MoveToDifferentContainerType) { + std::list> input; + input.push_back(std::make_unique(1)); + input.push_back(std::make_unique(2)); + + std::array, 3> actual; + absl::c_move(input, actual); + + EXPECT_EQ(input.front(), nullptr); + ASSERT_NE(actual[0], nullptr); + EXPECT_EQ(*actual[0], 1); +} + +TEST(MutatingTest, MoveToCArray) { + std::vector> input; + input.push_back(std::make_unique(1)); + input.push_back(std::make_unique(2)); + + std::unique_ptr actual[3]; + absl::c_move(input, actual); + + EXPECT_EQ(input.front(), nullptr); + ASSERT_NE(actual[0], nullptr); + EXPECT_EQ(*actual[0], 1); +} + +TEST(MutatingTest, MoveFromCArray) { + std::unique_ptr input[2]; + input[0] = std::make_unique(1); + input[1] = std::make_unique(2); + + std::vector> actual(3); + absl::c_move(input, actual); + + EXPECT_EQ(input[0], nullptr); + ASSERT_NE(actual[0], nullptr); + EXPECT_EQ(*actual[0], 1); +} + + +TEST(MutatingTest, MoveToCArrayInvalidSize) { + std::vector input = {1, 2, 3}; + int actual[2] = {0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_move(input, actual), ""); + } +#endif +} + +TEST(MutatingTest, MoveToContainerInvalidSize) { + std::list input = {1, 2, 3, 4, 5}; + std::list actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_move(input, actual), ""); + } +#endif +} + +TEST(MutatingTest, MoveToForwardListInvalidSize) { + std::forward_list input = {1, 2, 3, 4, 5}; + std::forward_list actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_move(input, actual), ""); + } +#endif +} + TEST(MutatingTest, SwapRanges) { std::vector odds = {2, 4, 6}; std::vector evens = {1, 3, 5}; absl::c_swap_ranges(odds, evens); EXPECT_THAT(odds, ElementsAre(1, 3, 5)); EXPECT_THAT(evens, ElementsAre(2, 4, 6)); + + odds.pop_back(); + absl::c_swap_ranges(odds, evens); + EXPECT_THAT(odds, ElementsAre(2, 4)); + EXPECT_THAT(evens, ElementsAre(1, 3, 6)); + + absl::c_swap_ranges(evens, odds); + EXPECT_THAT(odds, ElementsAre(1, 3)); + EXPECT_THAT(evens, ElementsAre(2, 4, 6)); } TEST_F(NonMutatingTest, Transform) { @@ -684,6 +1161,139 @@ TEST_F(NonMutatingTest, Transform) { EXPECT_EQ(std::vector({1, 5, 4}), z); *end = 7; EXPECT_EQ(std::vector({1, 5, 4, 7}), z); + + std::vector x2{1, 2, 3}; + std::vector y2{10, 20, 30}; + std::vector z2(3); + auto out_it = z2.begin(); + absl::c_transform(x2, y2, out_it, std::plus()); + // The caller's iterator should not be modified. + EXPECT_EQ(out_it, z2.begin()); + EXPECT_EQ(z2, std::vector({11, 22, 33})); +} + +TEST(MutatingTest, TransformToContainer) { + const std::vector input = {1, 2, 3}; + std::vector actual = {0, 0, 0, 4, 5}; + absl::c_transform(input, actual, [](int x) { return x * 2; }); + EXPECT_THAT(actual, ElementsAre(2, 4, 6, 4, 5)); +} + +TEST(MutatingTest, BinaryTransformToContainer) { + { + const std::vector input1 = {1, 2, 3}; + const std::vector input2 = {10, 20, 30}; + std::vector actual = {0, 0, 0, 4, 5}; + absl::c_transform(input1, input2, actual, std::plus()); + EXPECT_THAT(actual, ElementsAre(11, 22, 33, 4, 5)); + } + { + const std::vector input1 = {1, 2, 3, 4}; + const std::vector input2 = {10, 20}; + std::vector actual = {0, 0, 0, 4, 5}; + absl::c_transform(input1, input2, actual, std::plus()); + EXPECT_THAT(actual, ElementsAre(11, 22, 0, 4, 5)); + } + { + const std::vector input1 = {1, 2}; + const std::vector input2 = {10, 20, 30, 40}; + std::vector actual = {0, 0, 0, 4, 5}; + absl::c_transform(input1, input2, actual, std::plus()); + EXPECT_THAT(actual, ElementsAre(11, 22, 0, 4, 5)); + } +} + +TEST(MutatingTest, TransformToDifferentContainerType) { + const std::list input = {1, 2, 3}; + std::array actual = {0, 0, 0, 4, 5}; + absl::c_transform(input, actual, [](int x) { return x * 2; }); + EXPECT_THAT(actual, ElementsAre(2, 4, 6, 4, 5)); +} + +TEST(MutatingTest, TransformToCArray) { + const std::vector input = {1, 2, 3}; + int actual[5] = {0, 0, 0, 4, 5}; + absl::c_transform(input, actual, [](int x) { return x * 2; }); + EXPECT_THAT(actual, ElementsAre(2, 4, 6, 4, 5)); +} + +TEST(MutatingTest, TransformFromCArray) { + const int input[5] = {1, 2, 3, 4, 5}; + std::vector actual = {0, 0, 0, 0, 0}; + absl::c_transform(input, actual, [](int x) { return x * 2; }); + EXPECT_THAT(actual, ElementsAre(2, 4, 6, 8, 10)); +} + +TEST(MutatingTest, TransformToCArrayInvalidSize) { + const std::vector input = {1, 2, 3}; + int actual[2] = {0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_transform(input, actual, [](int x) { return x * 2; }), + ""); + } +#endif +} + +TEST(MutatingTest, BinaryTransformToCArrayInvalidSize) { + const std::vector input1 = {1, 2, 3}; + const std::vector input2 = {10, 20, 30}; + int actual[2] = {0, 0}; + (void)actual; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_transform(input1, input2, actual, std::plus()), + ""); + } +#endif +} + +TEST(MutatingTest, TransformToContainerInvalidSize) { + const std::list input = {1, 2, 3, 4, 5}; + std::list actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_transform(input, actual, [](int x) { return x * 2; }), + ""); + } +#endif +} + +TEST(MutatingTest, BinaryTransformToContainerInvalidSize) { + const std::vector input1 = {1, 2, 3, 4, 5}; + const std::vector input2 = {10, 20, 30, 40, 50}; + std::vector actual = {0, 0, 0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_transform(input1, input2, actual, std::plus()), + ""); + } +#endif +} + +TEST(MutatingTest, BinaryTransformInput2InvalidSize) { + const std::vector input1 = {1, 2, 3}; + const std::vector input2 = {10, 20}; + std::vector actual = {0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_transform(input1, input2, actual, std::plus()), + ""); + } +#endif +} + +TEST(MutatingTest, BinaryTransformInput1InvalidSize) { + const std::vector input1 = {1, 2}; + const std::vector input2 = {10, 20, 30}; + std::vector actual = {0}; +#if GTEST_HAS_DEATH_TEST + if (IsHardened()) { + EXPECT_DEATH(absl::c_transform(input1, input2, actual, std::plus()), + ""); + } +#endif } TEST(MutatingTest, Replace) { @@ -749,10 +1359,9 @@ MATCHER_P2(IsElement, key, value, "") { TEST(MutatingTest, StableSort) { std::vector test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}}; absl::c_stable_sort(test_vector); - EXPECT_THAT( - test_vector, - ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1), - IsElement(2, 0), IsElement(2, 2))); + EXPECT_THAT(test_vector, + ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1), + IsElement(2, 0), IsElement(2, 2))); } TEST(MutatingTest, StableSortWithPredicate) { @@ -760,10 +1369,9 @@ TEST(MutatingTest, StableSortWithPredicate) { absl::c_stable_sort(test_vector, [](const Element& e1, const Element& e2) { return e2 < e1; }); - EXPECT_THAT( - test_vector, - ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2), - IsElement(1, 1), IsElement(1, 0))); + EXPECT_THAT(test_vector, + ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2), + IsElement(1, 1), IsElement(1, 0))); } TEST(MutatingTest, ReplaceCopyIf) { @@ -781,12 +1389,24 @@ TEST(MutatingTest, Fill) { EXPECT_THAT(actual, ElementsAre(1, 1, 1, 1, 1)); } +TEST(MutatingTest, FillWithRvalue) { + std::vector actual(5); + absl::c_fill(absl::MakeSpan(actual), 1); + EXPECT_THAT(actual, ElementsAre(1, 1, 1, 1, 1)); +} + TEST(MutatingTest, FillN) { std::vector actual(5, 0); absl::c_fill_n(actual, 2, 1); EXPECT_THAT(actual, ElementsAre(1, 1, 0, 0, 0)); } +TEST(MutatingTest, FillNWithRvalue) { + std::vector actual(5, 0); + absl::c_fill_n(absl::MakeSpan(actual), 2, 1); + EXPECT_THAT(actual, ElementsAre(1, 1, 0, 0, 0)); +} + TEST(MutatingTest, Generate) { std::vector actual(5); int x = 0; @@ -863,10 +1483,18 @@ TEST(MutatingTest, RotateCopy) { TEST(MutatingTest, Shuffle) { std::vector actual = {1, 2, 3, 4, 5}; - absl::c_shuffle(actual, std::random_device()); + absl::c_shuffle(actual, absl::InsecureBitGen()); EXPECT_THAT(actual, UnorderedElementsAre(1, 2, 3, 4, 5)); } +TEST(MutatingTest, Sample) { + std::vector actual; + absl::c_sample(std::vector{1, 2, 3, 4, 5}, std::back_inserter(actual), 3, + absl::InsecureBitGen()); + EXPECT_THAT(actual, IsSubsetOf({1, 2, 3, 4, 5})); + EXPECT_THAT(actual, SizeIs(3)); +} + TEST(MutatingTest, PartialSort) { std::vector sequence{5, 3, 42, 0}; absl::c_partial_sort(sequence, sequence.begin() + 2); @@ -1022,4 +1650,1286 @@ TEST(MutatingTest, PermutationOperations) { EXPECT_EQ(initial, permuted); } +#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + +TEST(ConstexprTest, Distance) { + // Works at compile time with constexpr containers. + static_assert(absl::c_distance(std::array()) == 3); +} + +TEST(ConstexprTest, MinElement) { + constexpr std::array kArray = {1, 2, 3}; + static_assert(*absl::c_min_element(kArray) == 1); +} + +TEST(ConstexprTest, MinElementWithPredicate) { + constexpr std::array kArray = {1, 2, 3}; + static_assert(*absl::c_min_element(kArray, std::greater()) == 3); +} + +TEST(ConstexprTest, MaxElement) { + constexpr std::array kArray = {1, 2, 3}; + static_assert(*absl::c_max_element(kArray) == 3); +} + +TEST(ConstexprTest, MaxElementWithPredicate) { + constexpr std::array kArray = {1, 2, 3}; + static_assert(*absl::c_max_element(kArray, std::greater()) == 1); +} + +TEST(ConstexprTest, MinMaxElement) { + static constexpr std::array kArray = {1, 2, 3}; + constexpr auto kMinMaxPair = absl::c_minmax_element(kArray); + static_assert(*kMinMaxPair.first == 1); + static_assert(*kMinMaxPair.second == 3); +} + +TEST(ConstexprTest, MinMaxElementWithPredicate) { + static constexpr std::array kArray = {1, 2, 3}; + constexpr auto kMinMaxPair = + absl::c_minmax_element(kArray, std::greater()); + static_assert(*kMinMaxPair.first == 3); + static_assert(*kMinMaxPair.second == 1); +} +#endif // defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && + // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + +#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L + +TEST(ConstexprTest, LinearSearch) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_linear_search(kArray, 3)); + static_assert(!absl::c_linear_search(kArray, 4)); +} + +TEST(ConstexprTest, AllOf) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(!absl::c_all_of(kArray, [](int x) { return x > 1; })); + static_assert(absl::c_all_of(kArray, [](int x) { return x > 0; })); +} + +TEST(ConstexprTest, AnyOf) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_any_of(kArray, [](int x) { return x > 2; })); + static_assert(!absl::c_any_of(kArray, [](int x) { return x > 5; })); +} + +TEST(ConstexprTest, NoneOf) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(!absl::c_none_of(kArray, [](int x) { return x > 2; })); + static_assert(absl::c_none_of(kArray, [](int x) { return x > 5; })); +} + +TEST(ConstexprTest, ForEach) { + static constexpr std::array kArray = [] { + std::array array = {1, 2, 3}; + absl::c_for_each(array, [](int& x) { x += 1; }); + return array; + }(); + static_assert(kArray == std::array{2, 3, 4}); +} + +TEST(ConstexprTest, Find) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_find(kArray, 1) == kArray.begin()); + static_assert(absl::c_find(kArray, 4) == kArray.end()); +} + +TEST(ConstexprTest, Contains) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_contains(kArray, 1)); + static_assert(!absl::c_contains(kArray, 4)); +} + +TEST(ConstexprTest, FindIf) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_find_if(kArray, [](int x) { return x > 2; }) == + kArray.begin() + 2); + static_assert(absl::c_find_if(kArray, [](int x) { return x > 5; }) == + kArray.end()); +} + +TEST(ConstexprTest, FindIfNot) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_find_if_not(kArray, [](int x) { return x > 1; }) == + kArray.begin()); + static_assert(absl::c_find_if_not(kArray, [](int x) { return x > 0; }) == + kArray.end()); +} + +TEST(ConstexprTest, FindEnd) { + static constexpr std::array kHaystack = {1, 2, 3, 2, 3}; + static constexpr std::array kNeedle = {2, 3}; + static_assert(absl::c_find_end(kHaystack, kNeedle) == kHaystack.begin() + 3); +} + +TEST(ConstexprTest, FindFirstOf) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_find_first_of(kArray, kArray) == kArray.begin()); +} + +TEST(ConstexprTest, AdjacentFind) { + static constexpr std::array kArray = {1, 2, 2, 3}; + static_assert(absl::c_adjacent_find(kArray) == kArray.begin() + 1); +} + +TEST(ConstexprTest, AdjacentFindWithPredicate) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_adjacent_find(kArray, std::less()) == + kArray.begin()); +} + +TEST(ConstexprTest, Count) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_count(kArray, 1) == 1); + static_assert(absl::c_count(kArray, 2) == 1); + static_assert(absl::c_count(kArray, 3) == 1); + static_assert(absl::c_count(kArray, 4) == 0); +} + +TEST(ConstexprTest, CountIf) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_count_if(kArray, [](int x) { return x > 0; }) == 3); + static_assert(absl::c_count_if(kArray, [](int x) { return x > 1; }) == 2); +} + +TEST(ConstexprTest, Mismatch) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 2, 3}; + static constexpr std::array kArray3 = {2, 3, 4}; + static_assert(absl::c_mismatch(kArray1, kArray2) == + std::pair{kArray1.end(), kArray2.end()}); + static_assert(absl::c_mismatch(kArray1, kArray3) == + std::pair{kArray1.begin(), kArray3.begin()}); +} + +TEST(ConstexprTest, MismatchWithPredicate) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 2, 3}; + static constexpr std::array kArray3 = {2, 3, 4}; + static_assert(absl::c_mismatch(kArray1, kArray2, std::not_equal_to()) == + std::pair{kArray1.begin(), kArray2.begin()}); + static_assert(absl::c_mismatch(kArray1, kArray3, std::not_equal_to()) == + std::pair{kArray1.end(), kArray3.end()}); +} + +TEST(ConstexprTest, Equal) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 2, 3}; + static constexpr std::array kArray3 = {2, 3, 4}; + static_assert(absl::c_equal(kArray1, kArray2)); + static_assert(!absl::c_equal(kArray1, kArray3)); +} + +TEST(ConstexprTest, EqualWithPredicate) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 2, 3}; + static constexpr std::array kArray3 = {2, 3, 4}; + static_assert(!absl::c_equal(kArray1, kArray2, std::not_equal_to())); + static_assert(absl::c_equal(kArray1, kArray3, std::not_equal_to())); +} + +TEST(ConstexprTest, IsPermutation) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {3, 2, 1}; + static constexpr std::array kArray3 = {2, 3, 4}; + static_assert(absl::c_is_permutation(kArray1, kArray2)); + static_assert(!absl::c_is_permutation(kArray1, kArray3)); +} + +TEST(ConstexprTest, IsPermutationWithPredicate) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {3, 2, 1}; + static constexpr std::array kArray3 = {2, 3, 4}; + static_assert(absl::c_is_permutation(kArray1, kArray2, std::equal_to())); + static_assert( + !absl::c_is_permutation(kArray1, kArray3, std::equal_to())); +} + +TEST(ConstexprTest, Search) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 2, 3}; + static constexpr std::array kArray3 = {2, 3, 4}; + static_assert(absl::c_search(kArray1, kArray2) == kArray1.begin()); + static_assert(absl::c_search(kArray1, kArray3) == kArray1.end()); +} + +TEST(ConstexprTest, SearchWithPredicate) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 2, 3}; + static constexpr std::array kArray3 = {2, 3, 4}; + static_assert(absl::c_search(kArray1, kArray2, std::not_equal_to()) == + kArray1.end()); + static_assert(absl::c_search(kArray1, kArray3, std::not_equal_to()) == + kArray1.begin()); +} + +TEST(ConstexprTest, ContainsSubrange) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 2, 3}; + static constexpr std::array kArray3 = {2, 3, 4}; + static_assert(absl::c_contains_subrange(kArray1, kArray2)); + static_assert(!absl::c_contains_subrange(kArray1, kArray3)); +} + +TEST(ConstexprTest, ContainsSubrangeWithPredicate) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 2, 3}; + static constexpr std::array kArray3 = {2, 3, 4}; + static_assert( + !absl::c_contains_subrange(kArray1, kArray2, std::not_equal_to<>())); + static_assert( + absl::c_contains_subrange(kArray1, kArray3, std::not_equal_to<>())); +} + +TEST(ConstexprTest, SearchN) { + static constexpr std::array kArray = {1, 2, 2, 3}; + static_assert(absl::c_search_n(kArray, 1, 1) == kArray.begin()); + static_assert(absl::c_search_n(kArray, 2, 2) == kArray.begin() + 1); + static_assert(absl::c_search_n(kArray, 1, 4) == kArray.end()); +} + +TEST(ConstexprTest, SearchNWithPredicate) { + static constexpr std::array kArray = {1, 2, 2, 3}; + static_assert(absl::c_search_n(kArray, 1, 1, std::not_equal_to()) == + kArray.begin() + 1); + static_assert(absl::c_search_n(kArray, 2, 2, std::not_equal_to()) == + kArray.end()); + static_assert(absl::c_search_n(kArray, 1, 4, std::not_equal_to()) == + kArray.begin()); +} + +TEST(ConstexprTest, Copy) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayCopy = [] { + std::array array; + absl::c_copy(kArray, array.begin()); + return array; + }(); + static_assert(kArrayCopy == kArray); +} + +TEST(ConstexprTest, CopyN) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayCopy = [] { + std::array array; + absl::c_copy_n(kArray, 2, array.begin()); + return array; + }(); + static_assert(kArrayCopy == std::array{1, 2}); +} + +TEST(ConstexprTest, CopyIf) { + static constexpr std::array kArray = {1, 2, 3, 4}; + static constexpr auto kArrayCopy = [] { + std::array array; + absl::c_copy_if(kArray, array.begin(), [](int x) { return x > 1; }); + return array; + }(); + static_assert(kArrayCopy == std::array{2, 3, 4}); +} + +TEST(ConstexprTest, CopyBackward) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayCopy = [] { + std::array array; + absl::c_copy_backward(kArray, array.end()); + return array; + }(); + static_assert(kArrayCopy == kArray); +} + +TEST(ConstexprTest, Move) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayMove = [] { + std::array array; + absl::c_move(kArray, array.begin()); + return array; + }(); + static_assert(kArrayMove == kArray); +} + +TEST(ConstexprTest, MoveBackward) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayMove = [] { + std::array array; + absl::c_move_backward(kArray, array.end()); + return array; + }(); + static_assert(kArrayMove == kArray); +} + +TEST(ConstexprTest, SwapRanges) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {4, 5, 6}; + + static constexpr auto kSwapped = [] { + std::array arr1 = kArray1; + std::array arr2 = kArray2; + absl::c_swap_ranges(arr1, arr2); + return std::make_pair(arr1, arr2); + }(); + + static_assert(kSwapped.first == kArray2); + static_assert(kSwapped.second == kArray1); +} + +TEST(ConstexprTest, Transform) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayTransform = [] { + std::array array; + absl::c_transform(kArray, array.begin(), [](int x) { return x + 1; }); + return array; + }(); + static_assert(kArrayTransform == std::array{2, 3, 4}); +} + +TEST(ConstexprTest, Replace) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayReplace = [] { + std::array array = kArray; + absl::c_replace(array, 1, 4); + return array; + }(); + static_assert(kArrayReplace == std::array{4, 2, 3}); +} + +TEST(ConstexprTest, ReplaceIf) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayReplaceIf = [] { + std::array array = kArray; + absl::c_replace_if(array, [](int x) { return x == 1; }, 4); + return array; + }(); + static_assert(kArrayReplaceIf == std::array{4, 2, 3}); +} + +TEST(ConstexprTest, ReplaceCopy) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayReplaceCopy = [] { + std::array array; + absl::c_replace_copy(kArray, array.begin(), 1, 4); + return array; + }(); + static_assert(kArrayReplaceCopy == std::array{4, 2, 3}); +} + +TEST(ConstexprTest, ReplaceCopyIf) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayReplaceCopyIf = [] { + std::array array; + absl::c_replace_copy_if( + kArray, array.begin(), [](int x) { return x == 1; }, 4); + return array; + }(); + static_assert(kArrayReplaceCopyIf == std::array{4, 2, 3}); +} + +TEST(ConstexprTest, Fill) { + static constexpr auto kArrayFill = [] { + std::array array; + absl::c_fill(array, 4); + return array; + }(); + static_assert(kArrayFill == std::array{4, 4, 4}); +} + +TEST(ConstexprTest, FillN) { + static constexpr auto kArrayFillN = [] { + std::array array = {0, 0, 0}; + absl::c_fill_n(array, 2, 4); + return array; + }(); + static_assert(kArrayFillN == std::array{4, 4, 0}); +} + +TEST(ConstexprTest, Generate) { + static constexpr auto kArrayGenerate = [] { + std::array array; + absl::c_generate(array, []() { return 4; }); + return array; + }(); + static_assert(kArrayGenerate == std::array{4, 4, 4}); +} + +TEST(ConstexprTest, GenerateN) { + static constexpr auto kArrayGenerateN = [] { + std::array array = {0, 0, 0}; + absl::c_generate_n(array, 2, []() { return 4; }); + return array; + }(); + static_assert(kArrayGenerateN == std::array{4, 4, 0}); +} + +TEST(ConstexprTest, RemoveCopy) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayRemoveCopy = [] { + std::array array; + absl::c_remove_copy(kArray, array.begin(), 1); + return array; + }(); + static_assert(kArrayRemoveCopy == std::array{2, 3}); +} + +TEST(ConstexprTest, RemoveCopyIf) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayRemoveCopyIf = [] { + std::array array; + absl::c_remove_copy_if(kArray, array.begin(), [](int x) { return x == 1; }); + return array; + }(); + static_assert(kArrayRemoveCopyIf == std::array{2, 3}); +} + +TEST(ConstexprTest, UniqueCopy) { + static constexpr std::array kArray = {1, 2, 2, 3}; + static constexpr auto kArrayUniqueCopy = [] { + std::array array; + absl::c_unique_copy(kArray, array.begin()); + return array; + }(); + static_assert(kArrayUniqueCopy == std::array{1, 2, 3}); +} + +TEST(ConstexprTest, UniqueCopyWithPredicate) { + static constexpr std::array kArray = {1, 2, 2, 3}; + static constexpr auto kArrayUniqueCopy = [] { + std::array array; + absl::c_unique_copy(kArray, array.begin(), std::equal_to<>()); + return array; + }(); + static_assert(kArrayUniqueCopy == std::array{1, 2, 3}); +} + +TEST(ConstexprTest, Reverse) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayReverse = [] { + std::array array = kArray; + absl::c_reverse(array); + return array; + }(); + static_assert(kArrayReverse == std::array{3, 2, 1}); +} + +TEST(ConstexprTest, ReverseCopy) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayReverseCopy = [] { + std::array array; + absl::c_reverse_copy(kArray, array.begin()); + return array; + }(); + static_assert(kArrayReverseCopy == std::array{3, 2, 1}); +} + +TEST(ConstexprTest, Rotate) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayRotate = [] { + std::array array = kArray; + absl::c_rotate(array, array.begin() + 1); + return array; + }(); + static_assert(kArrayRotate == std::array{2, 3, 1}); +} + +TEST(ConstexprTest, RotateCopy) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayRotateCopy = [] { + std::array array; + absl::c_rotate_copy(kArray, kArray.begin() + 1, array.begin()); + return array; + }(); + static_assert(kArrayRotateCopy == std::array{2, 3, 1}); +} + +TEST(ConstexprTest, IsPartitioned) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(!absl::c_is_partitioned(kArray, [](int x) { return x > 1; })); + + static constexpr std::array kPartitionedArray = {2, 3, 1}; + static_assert( + absl::c_is_partitioned(kPartitionedArray, [](int x) { return x > 1; })); +} + +TEST(ConstexprTest, Partition) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayPartition = [] { + std::array array = kArray; + absl::c_partition(array, [](int x) { return x > 1; }); + return array; + }(); + static_assert( + absl::c_is_partitioned(kArrayPartition, [](int x) { return x > 1; })); +} + +TEST(ConstexprTest, PartitionCopy) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kPartitioned = [] { + std::array true_part; + std::array false_part; + absl::c_partition_copy(kArray, true_part.begin(), false_part.begin(), + [](int x) { return x > 1; }); + return std::make_pair(true_part, false_part); + }(); + static_assert(kPartitioned.first == std::array{2, 3}); + static_assert(kPartitioned.second == std::array{1}); +} + +TEST(ConstexprTest, PartitionPoint) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kPartitionPoint = + absl::c_partition_point(kArray, [](int x) { return x > 1; }); + static_assert(kPartitionPoint == kArray.end()); +} + +TEST(ConstexprTest, Sort) { + static constexpr std::array kArray = {2, 1, 3}; + static constexpr auto kArraySort = [] { + std::array array = kArray; + absl::c_sort(array); + return array; + }(); + static_assert(kArraySort == std::array{1, 2, 3}); +} + +TEST(ConstexprTest, SortWithPredicate) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArraySort = [] { + std::array array = kArray; + absl::c_sort(array, std::greater<>()); + return array; + }(); + static_assert(kArraySort == std::array{3, 2, 1}); +} + +TEST(ConstexprTest, IsSorted) { + static constexpr std::array kSortedArray = {1, 2, 3}; + static_assert(absl::c_is_sorted(kSortedArray)); + static constexpr std::array kUnsortedArray = {1, 3, 2}; + static_assert(!absl::c_is_sorted(kUnsortedArray)); +} + +TEST(ConstexprTest, IsSortedWithPredicate) { + static constexpr std::array kSortedArray = {3, 2, 1}; + static_assert(absl::c_is_sorted(kSortedArray, std::greater<>())); + static constexpr std::array kUnsortedArray = {1, 3, 2}; + static_assert(!absl::c_is_sorted(kUnsortedArray, std::greater<>())); +} + +TEST(ConstexprTest, PartialSort) { + static constexpr std::array kArray = {3, 1, 4, 2}; + static constexpr auto kArrayPartialSort = [] { + std::array array = kArray; + absl::c_partial_sort(array, array.begin() + 2); + return array; + }(); + static_assert(kArrayPartialSort[0] == 1); + static_assert(kArrayPartialSort[1] == 2); +} + +TEST(ConstexprTest, PartialSortWithPredicate) { + static constexpr std::array kArray = {3, 1, 4, 2}; + static constexpr auto kArrayPartialSort = [] { + std::array array = kArray; + absl::c_partial_sort(array, array.begin() + 2, std::greater<>()); + return array; + }(); + static_assert(kArrayPartialSort[0] == 4); + static_assert(kArrayPartialSort[1] == 3); +} + +TEST(ConstexprTest, PartialSortCopy) { + static constexpr std::array kArray = {3, 1, 4, 2}; + static constexpr auto kArrayPartialSort = [] { + std::array array; + absl::c_partial_sort_copy(kArray, array); + return array; + }(); + static_assert(kArrayPartialSort[0] == 1); + static_assert(kArrayPartialSort[1] == 2); +} + +TEST(ConstexprTest, PartialSortCopyWithPredicate) { + static constexpr std::array kArray = {3, 1, 4, 2}; + static constexpr auto kArrayPartialSort = [] { + std::array array; + absl::c_partial_sort_copy(kArray, array, std::greater<>()); + return array; + }(); + static_assert(kArrayPartialSort[0] == 4); + static_assert(kArrayPartialSort[1] == 3); +} + +TEST(ConstexprTest, IsSortedUntil) { + static constexpr std::array kSortedArray = {1, 2, 3}; + static_assert(absl::c_is_sorted_until(kSortedArray) == kSortedArray.end()); + static constexpr std::array kUnsortedArray = {1, 3, 2}; + static_assert(absl::c_is_sorted_until(kUnsortedArray) == + kUnsortedArray.begin() + 2); +} + +TEST(ConstexprTest, IsSortedUntilWithPredicate) { + static constexpr std::array kSortedArray = {3, 2, 1}; + static_assert(absl::c_is_sorted_until(kSortedArray, std::greater<>()) == + kSortedArray.end()); + static constexpr std::array kUnsortedArray = {1, 3, 2}; + static_assert(absl::c_is_sorted_until(kUnsortedArray, std::greater<>()) == + kUnsortedArray.begin() + 1); +} + +TEST(ConstexprTest, NthElement) { + static constexpr std::array kArray = {2, 1, 3, 4}; + static constexpr auto kArrayNthElement = [] { + std::array array = kArray; + absl::c_nth_element(array, array.begin() + 2); + return array; + }(); + static_assert(kArrayNthElement[2] == 3); + static_assert(kArrayNthElement[0] <= kArrayNthElement[2]); + static_assert(kArrayNthElement[1] <= kArrayNthElement[2]); + static_assert(kArrayNthElement[3] >= kArrayNthElement[2]); +} + +TEST(ConstexprTest, NthElementWithPredicate) { + static constexpr std::array kArray = {1, 2, 3, 4}; + static constexpr auto kArrayNthElement = [] { + std::array array = kArray; + absl::c_nth_element(array, array.begin() + 2, std::greater<>()); + return array; + }(); + static_assert(kArrayNthElement[2] == 2); + static_assert(std::greater<>()(kArrayNthElement[0], kArrayNthElement[2]) || + kArrayNthElement[0] == kArrayNthElement[2]); + static_assert(std::greater<>()(kArrayNthElement[1], kArrayNthElement[2]) || + kArrayNthElement[1] == kArrayNthElement[2]); + static_assert(std::greater<>()(kArrayNthElement[2], kArrayNthElement[3]) || + kArrayNthElement[2] == kArrayNthElement[3]); +} + +TEST(ConstexprTest, LowerBound) { + static constexpr std::array kArray = {1, 2, 3, 4}; + static constexpr auto kLowerBound = absl::c_lower_bound(kArray, 2); + static_assert(kLowerBound == kArray.begin() + 1); +} + +TEST(ConstexprTest, LowerBoundWithPredicate) { + static constexpr std::array kArray = {4, 3, 2, 1}; + static constexpr auto kLowerBound = + absl::c_lower_bound(kArray, 2, std::greater<>()); + static_assert(kLowerBound == kArray.begin() + 2); +} + +TEST(ConstexprTest, UpperBound) { + static constexpr std::array kArray = {1, 2, 3, 4}; + static constexpr auto kUpperBound = absl::c_upper_bound(kArray, 2); + static_assert(kUpperBound == kArray.begin() + 2); +} + +TEST(ConstexprTest, UpperBoundWithPredicate) { + static constexpr std::array kArray = {4, 3, 2, 1}; + static constexpr auto kUpperBound = + absl::c_upper_bound(kArray, 2, std::greater<>()); + static_assert(kUpperBound == kArray.begin() + 3); +} + +TEST(ConstexprTest, EqualRange) { + static constexpr std::array kArray = {1, 2, 3, 4}; + static constexpr auto kEqualRange = absl::c_equal_range(kArray, 2); + static_assert(kEqualRange.first == kArray.begin() + 1); + static_assert(kEqualRange.second == kArray.begin() + 2); +} + +TEST(ConstexprTest, EqualRangeWithPredicate) { + static constexpr std::array kArray = {4, 3, 2, 1}; + static constexpr auto kEqualRange = + absl::c_equal_range(kArray, 2, std::greater<>()); + static_assert(kEqualRange.first == kArray.begin() + 2); + static_assert(kEqualRange.second == kArray.begin() + 3); +} + +TEST(ConstexprTest, BinarySearch) { + static constexpr std::array kArray = {1, 2, 3, 4}; + static constexpr bool kBinarySearch = absl::c_binary_search(kArray, 2); + static_assert(kBinarySearch); +} + +TEST(ConstexprTest, BinarySearchWithPredicate) { + static constexpr std::array kArray = {4, 3, 2, 1}; + static constexpr bool kBinarySearch = + absl::c_binary_search(kArray, 2, std::greater<>()); + static_assert(kBinarySearch); +} + +TEST(ConstexprTest, Merge) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {4, 5, 6}; + static constexpr auto kArrayMerge = [] { + std::array array; + absl::c_merge(kArray1, kArray2, array.begin()); + return array; + }(); + static_assert(kArrayMerge == std::array{1, 2, 3, 4, 5, 6}); +} + +TEST(ConstexprTest, MergeWithPredicate) { + static constexpr std::array kArray1 = {3, 2, 1}; + static constexpr std::array kArray2 = {6, 5, 4}; + static constexpr auto kArrayMerge = [] { + std::array array; + absl::c_merge(kArray1, kArray2, array.begin(), std::greater<>()); + return array; + }(); + static_assert(kArrayMerge == std::array{6, 5, 4, 3, 2, 1}); +} + +TEST(ConstexprTest, Includes) { + static constexpr std::array kArray1 = {1, 2, 3, 4, 5, 6}; + static constexpr std::array kArray2 = {2, 3, 5}; + static constexpr bool kIncludes = absl::c_includes(kArray1, kArray2); + static_assert(kIncludes); +} + +TEST(ConstexprTest, IncludesWithPredicate) { + static constexpr std::array kArray1 = {6, 5, 4, 3, 2, 1}; + static constexpr std::array kArray2 = {5, 3, 2}; + static constexpr bool kIncludes = + absl::c_includes(kArray1, kArray2, std::greater<>()); + static_assert(kIncludes); +} + +TEST(ConstexprTest, SetUnion) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 3, 4}; + static constexpr auto kArraySetUnion = [] { + std::array array; + absl::c_set_union(kArray1, kArray2, array.begin()); + return array; + }(); + static_assert(kArraySetUnion == std::array{1, 2, 3, 4}); +} + +TEST(ConstexprTest, SetUnionWithPredicate) { + static constexpr std::array kArray1 = {3, 2, 1}; + static constexpr std::array kArray2 = {4, 3, 1}; + static constexpr auto kArraySetUnion = [] { + std::array array; + absl::c_set_union(kArray1, kArray2, array.begin(), std::greater<>()); + return array; + }(); + static_assert(kArraySetUnion == std::array{4, 3, 2, 1}); +} + +TEST(ConstexprTest, SetIntersection) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 3, 4}; + static constexpr auto kArraySetIntersection = [] { + std::array array; + absl::c_set_intersection(kArray1, kArray2, array.begin()); + return array; + }(); + static_assert(kArraySetIntersection == std::array{1, 3}); +} + +TEST(ConstexprTest, SetIntersectionWithPredicate) { + static constexpr std::array kArray1 = {3, 2, 1}; + static constexpr std::array kArray2 = {4, 3, 1}; + static constexpr auto kArraySetIntersection = [] { + std::array array; + absl::c_set_intersection(kArray1, kArray2, array.begin(), std::greater<>()); + return array; + }(); + static_assert(kArraySetIntersection == std::array{3, 1}); +} + +TEST(ConstexprTest, SetDifference) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 3, 4}; + static constexpr auto kArraySetDifference = [] { + std::array array; + absl::c_set_difference(kArray1, kArray2, array.begin()); + return array; + }(); + static_assert(kArraySetDifference == std::array{2}); +} + +TEST(ConstexprTest, SetDifferenceWithPredicate) { + static constexpr std::array kArray1 = {3, 2, 1}; + static constexpr std::array kArray2 = {4, 3, 1}; + static constexpr auto kArraySetDifference = [] { + std::array array; + absl::c_set_difference(kArray1, kArray2, array.begin(), std::greater<>()); + return array; + }(); + static_assert(kArraySetDifference == std::array{2}); +} + +TEST(ConstexprTest, SetSymmetricDifference) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 3, 4}; + static constexpr auto kArraySetSymmetricDifference = [] { + std::array array; + absl::c_set_symmetric_difference(kArray1, kArray2, array.begin()); + return array; + }(); + static_assert(kArraySetSymmetricDifference == std::array{2, 4}); +} + +TEST(ConstexprTest, SetSymmetricDifferenceWithPredicate) { + static constexpr std::array kArray1 = {3, 2, 1}; + static constexpr std::array kArray2 = {4, 3, 1}; + static constexpr auto kArraySetSymmetricDifference = [] { + std::array array; + absl::c_set_symmetric_difference(kArray1, kArray2, array.begin(), + std::greater<>()); + return array; + }(); + static_assert(kArraySetSymmetricDifference == std::array{4, 2}); +} + +TEST(ConstexprTest, PushHeap) { + static constexpr auto kArray = [] { + std::array array = {1, 2, 3, 4}; + absl::c_push_heap(array); + return array; + }(); + static_assert(kArray[0] == 4); +} + +TEST(ConstexprTest, PushHeapWithPredicate) { + static constexpr auto kArray = [] { + std::array array = {4, 3, 2, 1}; + absl::c_push_heap(array, std::greater<>()); + return array; + }(); + static_assert(kArray[0] == 1); +} + +TEST(ConstexprTest, PopHeap) { + static constexpr auto kArray = [] { + std::array array = {4, 3, 2, 1}; + absl::c_pop_heap(array); + return array; + }(); + static_assert(kArray[3] == 4); +} + +TEST(ConstexprTest, PopHeapWithPredicate) { + static constexpr auto kArray = [] { + std::array array = {1, 2, 3, 4}; + absl::c_pop_heap(array, std::greater<>()); + return array; + }(); + static_assert(kArray[3] == 1); +} + +TEST(ConstexprTest, MakeHeap) { + static constexpr auto kArray = [] { + std::array array = {1, 2, 3, 4}; + absl::c_make_heap(array); + return array; + }(); + static_assert(absl::c_is_heap(kArray)); +} + +TEST(ConstexprTest, MakeHeapWithPredicate) { + static constexpr auto kArray = [] { + std::array array = {4, 3, 2, 1}; + absl::c_make_heap(array, std::greater<>()); + return array; + }(); + static_assert(absl::c_is_heap(kArray, std::greater<>())); +} + +TEST(ConstexprTest, SortHeap) { + static constexpr auto kArray = [] { + std::array array = {1, 2, 3, 4}; + absl::c_make_heap(array); + absl::c_sort_heap(array); + return array; + }(); + static_assert(kArray == std::array{1, 2, 3, 4}); +} + +TEST(ConstexprTest, SortHeapWithPredicate) { + static constexpr auto kArray = [] { + std::array array = {4, 3, 2, 1}; + absl::c_make_heap(array, std::greater<>()); + absl::c_sort_heap(array, std::greater<>()); + return array; + }(); + static_assert(kArray == std::array{4, 3, 2, 1}); +} + +TEST(ConstexprTest, IsHeap) { + static constexpr std::array kHeap = {4, 2, 3, 1}; + static_assert(absl::c_is_heap(kHeap)); + static constexpr std::array kNotHeap = {1, 2, 3, 4}; + static_assert(!absl::c_is_heap(kNotHeap)); +} + +TEST(ConstexprTest, IsHeapWithPredicate) { + static constexpr std::array kHeap = {1, 2, 3, 4}; + static_assert(absl::c_is_heap(kHeap, std::greater<>())); + static constexpr std::array kNotHeap = {4, 3, 2, 1}; + static_assert(!absl::c_is_heap(kNotHeap, std::greater<>())); +} + +TEST(ConstexprTest, IsHeapUntil) { + static constexpr std::array kHeap = {4, 2, 3, 1}; + static_assert(absl::c_is_heap_until(kHeap) == kHeap.end()); + static constexpr std::array kNotHeap = {4, 2, 3, 5}; + static_assert(absl::c_is_heap_until(kNotHeap) == kNotHeap.begin() + 3); +} + +TEST(ConstexprTest, IsHeapUntilWithPredicate) { + static constexpr std::array kHeap = {1, 2, 3, 4}; + static_assert(absl::c_is_heap_until(kHeap, std::greater<>()) == kHeap.end()); + static constexpr std::array kNotHeap = {1, 2, 3, 0}; + static_assert(absl::c_is_heap_until(kNotHeap, std::greater<>()) == + kNotHeap.begin() + 3); +} + +TEST(ConstexprTest, LexicographicalCompare) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 2, 4}; + static constexpr std::array kArray3 = {1, 2, 3}; + static_assert(absl::c_lexicographical_compare(kArray1, kArray2)); + static_assert(!absl::c_lexicographical_compare(kArray2, kArray1)); + static_assert(!absl::c_lexicographical_compare(kArray1, kArray3)); +} + +TEST(ConstexprTest, LexicographicalCompareWithPredicate) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {1, 2, 4}; + static constexpr std::array kArray3 = {1, 2, 3}; + static_assert( + !absl::c_lexicographical_compare(kArray1, kArray2, std::greater<>())); + static_assert( + absl::c_lexicographical_compare(kArray2, kArray1, std::greater<>())); + static_assert( + !absl::c_lexicographical_compare(kArray1, kArray3, std::greater<>())); +} + +TEST(ConstexprTest, NextPermutation) { + static constexpr auto kArray = [] { + std::array array = {1, 2, 3}; + absl::c_next_permutation(array); + return array; + }(); + static_assert(kArray == std::array{1, 3, 2}); +} + +TEST(ConstexprTest, NextPermutationWithPredicate) { + static constexpr auto kArray = [] { + std::array array = {3, 2, 1}; + absl::c_next_permutation(array, std::greater<>()); + return array; + }(); + static_assert(kArray == std::array{3, 1, 2}); +} + +TEST(ConstexprTest, PrevPermutation) { + static constexpr auto kArray = [] { + std::array array = {1, 3, 2}; + absl::c_prev_permutation(array); + return array; + }(); + static_assert(kArray == std::array{1, 2, 3}); +} + +TEST(ConstexprTest, PrevPermutationWithPredicate) { + static constexpr auto kArray = [] { + std::array array = {1, 2, 3}; + absl::c_prev_permutation(array, std::greater<>()); + return array; + }(); + static_assert(kArray == std::array{1, 3, 2}); +} + +TEST(ConstexprTest, Iota) { + static constexpr auto kArray = [] { + std::array array; + absl::c_iota(array, 1); + return array; + }(); + static_assert(kArray == std::array{1, 2, 3}); +} + +TEST(ConstexprTest, Accumulate) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_accumulate(kArray, 0) == 6); +} + +TEST(ConstexprTest, AccumulateWithPredicate) { + static constexpr std::array kArray = {1, 2, 3}; + static_assert(absl::c_accumulate(kArray, 1, std::multiplies<>()) == 6); +} + +TEST(ConstexprTest, InnerProduct) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {4, 5, 6}; + static_assert(absl::c_inner_product(kArray1, kArray2, 0) == 32); +} + +TEST(ConstexprTest, InnerProductWithPredicate) { + static constexpr std::array kArray1 = {1, 2, 3}; + static constexpr std::array kArray2 = {4, 5, 6}; + static_assert(absl::c_inner_product(kArray1, kArray2, 1, std::multiplies<>(), + std::plus<>()) == 315); +} + +TEST(ConstexprTest, AdjacentDifference) { + static constexpr std::array kArray = {1, 2, 4}; + static constexpr auto kArrayAdjacentDifference = [] { + std::array array; + absl::c_adjacent_difference(kArray, array.begin()); + return array; + }(); + static_assert(kArrayAdjacentDifference == std::array{1, 1, 2}); +} + +TEST(ConstexprTest, AdjacentDifferenceWithPredicate) { + static constexpr std::array kArray = {1, 2, 4}; + static constexpr auto kArrayAdjacentDifference = [] { + std::array array; + absl::c_adjacent_difference(kArray, array.begin(), std::multiplies<>()); + return array; + }(); + static_assert(kArrayAdjacentDifference == std::array{1, 2, 8}); +} + +TEST(ConstexprTest, PartialSum) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayPartialSum = [] { + std::array array; + absl::c_partial_sum(kArray, array.begin()); + return array; + }(); + static_assert(kArrayPartialSum == std::array{1, 3, 6}); +} + +TEST(ConstexprTest, PartialSumWithPredicate) { + static constexpr std::array kArray = {1, 2, 3}; + static constexpr auto kArrayPartialSum = [] { + std::array array; + absl::c_partial_sum(kArray, array.begin(), std::multiplies<>()); + return array; + }(); + static_assert(kArrayPartialSum == std::array{1, 2, 6}); +} + +#endif // defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && + // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L + +// A type that acts as both a container and an iterator. +struct AmbiguousType { + // Container requirements + int* begin() { return nullptr; } + int* end() { return nullptr; } + + // Iterator requirements + using iterator_category = std::input_iterator_tag; + using value_type = int; + using difference_type = std::ptrdiff_t; + using pointer = int*; + using reference = int&; + + int& operator*() { + static int x; + return x; + } + AmbiguousType& operator++() { return *this; } + AmbiguousType operator++(int) { return *this; } + friend bool operator==(const AmbiguousType&, const AmbiguousType&) { + return true; + } + friend bool operator!=(const AmbiguousType&, const AmbiguousType&) { + return false; + } +}; + +template +struct CanCopy : std::false_type {}; +template +struct CanCopy(), + std::declval()))>> + : std::true_type {}; + +template +struct CanCopyN : std::false_type {}; +template +struct CanCopyN(), std::declval(), + std::declval()))>> : std::true_type {}; + +template +struct CanMove : std::false_type {}; +template +struct CanMove(), + std::declval()))>> + : std::true_type {}; + +template +struct CanTransformUnary : std::false_type {}; +template +struct CanTransformUnary(), std::declval(), + std::declval()))>> : std::true_type {}; + +template +struct CanTransformBinary : std::false_type {}; +template +struct CanTransformBinary< + Container1, Container2, Output, BinaryOp, + absl::void_t(), std::declval(), + std::declval(), std::declval()))>> : std::true_type { +}; + +TEST(CanCopyTest, CopyToMultiDimArray) { + static_assert(CanCopy, int (&)[10]>::value); + static_assert(!CanCopy, int (&)[2][2]>::value); + static_assert(CanCopyN, int (&)[10]>::value); + static_assert(!CanCopyN, int (&)[2][2]>::value); + + static_assert(CanCopy::value); + static_assert(!CanCopy::value); + static_assert(CanCopyN::value); + static_assert(!CanCopyN::value); + static_assert(!CanCopy::value); + static_assert(!CanCopy::value); + static_assert(!CanCopyN::value); + static_assert(!CanCopyN::value); +} + +TEST(CanCopyTest, BlockNonWritableIterators) { + using Vec = std::vector; + + static_assert(CanCopy::value); + static_assert(CanCopy>::value); +} + +TEST(CanCopyTest, AmbiguousTypeFailsToCompile) { + using Vec = std::vector; + // Because AmbiguousType is both an iterator and a container, + // the compiler should fail to resolve the c_copy overload. + static_assert(!CanCopy::value, + "Ambiguous types should not compile!"); + static_assert(!CanCopyN::value, + "Ambiguous types should not compile!"); +} + +TEST(CanCopyTest, CopyToRValue) { + using Vec = std::vector; + using Span = absl::Span; + static_assert(CanCopy::value, + "Should be able to copy to rvalue Span"); + static_assert(!CanCopy::value, + "Should not be able to copy to rvalue vector"); + static_assert(CanCopyN::value, + "Should be able to copy_n to rvalue Span"); + static_assert(!CanCopyN::value, + "Should not be able to copy_n to rvalue vector"); +} + +TEST(CanMoveTest, MoveToMultiDimArray) { + static_assert(CanMove, int (&)[10]>::value); + static_assert(!CanMove, int (&)[2][2]>::value); + + static_assert(CanMove::value); + static_assert(!CanMove::value); + static_assert(!CanMove::value); + static_assert(!CanMove::value); +} + +TEST(CanMoveTest, AmbiguousTypeFailsToCompile) { + using Vec = std::vector; + // Because AmbiguousType is both an iterator and a container, + // the compiler should fail to resolve the c_move overload. + static_assert(!CanMove::value, + "Ambiguous types should not compile!"); +} + +TEST(CanMoveTest, MoveToRValue) { + using Vec = std::vector>; + using Span = absl::Span>; + static_assert(CanMove::value, + "Should be able to move to rvalue Span"); + static_assert(!CanMove::value, + "Should not be able to move to rvalue vector"); +} + +TEST(CanTransformTest, TransformToMultiDimArray) { + using Negate = std::negate; + using Plus = std::plus; + + static_assert( + CanTransformUnary, int (&)[10], Negate>::value); + static_assert( + !CanTransformUnary, int (&)[2][2], Negate>::value); + + static_assert(CanTransformUnary::value); + static_assert(!CanTransformUnary::value); + static_assert(!CanTransformUnary::value); + static_assert(!CanTransformUnary::value); + + static_assert(CanTransformBinary, std::vector, + int (&)[10], Plus>::value); + static_assert(!CanTransformBinary, std::vector, + int (&)[2][2], Plus>::value); + + static_assert(CanTransformBinary::value); + static_assert( + !CanTransformBinary::value); + static_assert( + !CanTransformBinary::value); + static_assert( + !CanTransformBinary::value); +} + +TEST(CanTransformTest, AmbiguousTypeFailsToCompile) { + using Vec = std::vector; + using Negate = std::negate; + using Plus = std::plus; + + // Because AmbiguousType is both an iterator and a container, + // the compiler should fail to resolve the c_transform overload. + static_assert(!CanTransformUnary::value, + "Ambiguous types should not compile!"); + static_assert(!CanTransformBinary::value, + "Ambiguous types should not compile!"); +} + +template +struct CanFill : std::false_type {}; + +template +struct CanFill()(), + std::declval()()))>> + : std::true_type {}; + +TEST(CanFillTest, NonSpans) { + using T = int; + + struct AbslSpanSubclass : absl::Span {}; + static_assert(!CanFill, T>::value, + "non-spans must not be allowed"); + static_assert(!CanFill::value, + "subclasses must not be allowed"); + +#ifdef __cpp_lib_span + struct StdSpanSubclass : std::span {}; + static_assert(!CanFill::value, + "std::span must not be allowed"); +#endif +} } // namespace diff --git a/absl/algorithm/equal_benchmark.cc b/absl/algorithm/equal_benchmark.cc deleted file mode 100644 index 7bf62c9a7f5..00000000000 --- a/absl/algorithm/equal_benchmark.cc +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// 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 -// -// https://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. - -#include -#include - -#include "benchmark/benchmark.h" -#include "absl/algorithm/algorithm.h" - -namespace { - -// The range of sequence sizes to benchmark. -constexpr int kMinBenchmarkSize = 1024; -constexpr int kMaxBenchmarkSize = 8 * 1024 * 1024; - -// A user-defined type for use in equality benchmarks. Note that we expect -// std::memcmp to win for this type: libstdc++'s std::equal only defers to -// memcmp for integral types. This is because it is not straightforward to -// guarantee that std::memcmp would produce a result "as-if" compared by -// operator== for other types (example gotchas: NaN floats, structs with -// padding). -struct EightBits { - explicit EightBits(int /* unused */) : data(0) {} - bool operator==(const EightBits& rhs) const { return data == rhs.data; } - uint8_t data; -}; - -template -void BM_absl_equal_benchmark(benchmark::State& state) { - std::vector xs(state.range(0), T(0)); - std::vector ys = xs; - while (state.KeepRunning()) { - const bool same = absl::equal(xs.begin(), xs.end(), ys.begin(), ys.end()); - benchmark::DoNotOptimize(same); - } -} - -template -void BM_std_equal_benchmark(benchmark::State& state) { - std::vector xs(state.range(0), T(0)); - std::vector ys = xs; - while (state.KeepRunning()) { - const bool same = std::equal(xs.begin(), xs.end(), ys.begin()); - benchmark::DoNotOptimize(same); - } -} - -template -void BM_memcmp_benchmark(benchmark::State& state) { - std::vector xs(state.range(0), T(0)); - std::vector ys = xs; - while (state.KeepRunning()) { - const bool same = - std::memcmp(xs.data(), ys.data(), xs.size() * sizeof(T)) == 0; - benchmark::DoNotOptimize(same); - } -} - -// The expectation is that the compiler should be able to elide the equality -// comparison altogether for sufficiently simple types. -template -void BM_absl_equal_self_benchmark(benchmark::State& state) { - std::vector xs(state.range(0), T(0)); - while (state.KeepRunning()) { - const bool same = absl::equal(xs.begin(), xs.end(), xs.begin(), xs.end()); - benchmark::DoNotOptimize(same); - } -} - -BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint8_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint8_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint8_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint8_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); - -BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint16_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint16_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint16_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint16_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); - -BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint32_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint32_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint32_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint32_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); - -BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint64_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint64_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint64_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint64_t) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); - -BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, EightBits) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_std_equal_benchmark, EightBits) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_memcmp_benchmark, EightBits) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); -BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, EightBits) - ->Range(kMinBenchmarkSize, kMaxBenchmarkSize); - -} // namespace diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 57caa0656e5..c0f276c16bd 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -14,7 +14,9 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") +load("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_cc//cc:cc_test.bzl", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -22,9 +24,16 @@ load( "ABSL_TEST_COPTS", ) -package(default_visibility = ["//visibility:public"]) +package( + default_visibility = ["//visibility:private"], + features = [ + "header_modules", + "layering_check", + "parse_headers", + ], +) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "atomic_hook", @@ -34,6 +43,56 @@ cc_library( visibility = [ "//absl:__subpackages__", ], + deps = [ + ":config", + ":core_headers", + ], +) + +cc_library( + name = "errno_saver", + hdrs = ["internal/errno_saver.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [":config"], +) + +cc_library( + name = "cpu_detect", + srcs = [ + "internal/cpu_detect.cc", + ], + hdrs = ["internal/cpu_detect.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/util/sysinfo/topology:__subpackages__", + ], + deps = [ + ":config", + ], +) + +cc_library( + name = "hardening", + hdrs = [ + "internal/hardening.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/util/coding:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/util/gtl:__subpackages__", + ], + deps = [ + ":config", + ":core_headers", + ], ) cc_library( @@ -42,7 +101,46 @@ cc_library( hdrs = ["log_severity.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - deps = [":core_headers"], + visibility = ["//visibility:public"], + deps = [ + ":config", + ":core_headers", + ], +) + +cc_library( + name = "no_destructor", + hdrs = ["no_destructor.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], + deps = [ + ":config", + ":nullability", + ], +) + +cc_library( + name = "nullability", + hdrs = ["nullability.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], + deps = [":config"], +) + +cc_library( + name = "nullability_traits_internal", + hdrs = ["internal/nullability_traits.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":config", + ":nullability", + ], ) cc_library( @@ -53,11 +151,19 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/base:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/concurrent/percpu:__pkg__", + "@do_not_use_for_gloop_visibility_only//gloop/concurrent/rcu:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/strings:__pkg__", + "@do_not_use_for_gloop_visibility_only//gloop/thread/fiber:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/util/hash:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/util/process:__subpackages__", ], deps = [ ":atomic_hook", ":config", ":core_headers", + ":errno_saver", ":log_severity", ], ) @@ -71,42 +177,71 @@ cc_library( "internal/spinlock_wait.cc", "internal/spinlock_win32.inc", ], - hdrs = [ - "internal/scheduling_mode.h", - "internal/spinlock_wait.h", - ], + hdrs = ["internal/spinlock_wait.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/base:__pkg__", + "@do_not_use_for_gloop_visibility_only//gloop/base:__pkg__", + "@do_not_use_for_gloop_visibility_only//gloop/thread/fiber:__pkg__", + ], + deps = [ + ":base_internal", + ":core_headers", + ":errno_saver", ], - deps = [":core_headers"], ) cc_library( name = "config", hdrs = [ "config.h", + "options.h", "policy_checks.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], ) cc_library( - name = "dynamic_annotations", - srcs = ["dynamic_annotations.cc"], - hdrs = ["dynamic_annotations.h"], + name = "cycleclock_internal", + hdrs = [ + "internal/cycleclock_config.h", + "internal/unscaledcycleclock_config.h", + ], copts = ABSL_DEFAULT_COPTS, - defines = ["__CLANG_SUPPORT_DYN_ANNOTATION__"], linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/base:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/util/random:__pkg__", + ], + deps = [ + ":base_internal", + ":config", + ], ) cc_library( - name = "core_headers", + name = "dynamic_annotations", srcs = [ - "internal/thread_annotations.h", + "internal/dynamic_annotations.h", + ], + hdrs = [ + "dynamic_annotations.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], + deps = [ + ":config", + ":core_headers", ], +) + +cc_library( + name = "core_headers", hdrs = [ "attributes.h", "const_init.h", @@ -117,6 +252,7 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], deps = [ ":config", ], @@ -131,21 +267,25 @@ cc_library( "internal/direct_mmap.h", "internal/low_level_alloc.h", ], - copts = ABSL_DEFAULT_COPTS, + copts = ABSL_DEFAULT_COPTS + select({ + "//conditions:default": [], + }), linkopts = select({ - "//absl:windows": [], + "@rules_cc//cc/compiler:msvc-cl": [], + "@rules_cc//cc/compiler:clang-cl": [], + "@rules_cc//cc/compiler:emscripten": [], "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, visibility = [ - "//absl:__subpackages__", + "//visibility:public", ], deps = [ ":base", + ":base_internal", ":config", ":core_headers", ":dynamic_annotations", ":raw_logging_internal", - ":spinlock_wait", ], ) @@ -153,17 +293,16 @@ cc_library( name = "base_internal", hdrs = [ "internal/hide_ptr.h", - "internal/identity.h", - "internal/inline_variable.h", - "internal/invoke.h", "internal/scheduling_mode.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/base:__subpackages__", ], deps = [ + ":config", "//absl/meta:type_traits", ], ) @@ -171,6 +310,7 @@ cc_library( cc_library( name = "base", srcs = [ + "casts.cc", "internal/cycleclock.cc", "internal/spinlock.cc", "internal/sysinfo.cc", @@ -191,22 +331,46 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = select({ - "//absl:windows": [], + "@rules_cc//cc/compiler:msvc-cl": [ + "-DEFAULTLIB:advapi32.lib", + ], + "@rules_cc//cc/compiler:clang-cl": [ + "-DEFAULTLIB:advapi32.lib", + ], + "//absl:mingw_compiler": [ + "-DEFAULTLIB:advapi32.lib", + "-ladvapi32", + ], + "@rules_cc//cc/compiler:emscripten": [], "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], deps = [ ":atomic_hook", ":base_internal", ":config", ":core_headers", - ":dynamic_annotations", - ":log_severity", + ":cycleclock_internal", + ":nullability", ":raw_logging_internal", ":spinlock_wait", "//absl/meta:type_traits", ], ) +cc_library( + name = "atomic_hook_test_helper", + testonly = True, + srcs = ["internal/atomic_hook_test_helper.cc"], + hdrs = ["internal/atomic_hook_test_helper.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":atomic_hook", + ":core_headers", + ], +) + cc_test( name = "atomic_hook_test", size = "small", @@ -215,8 +379,10 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":atomic_hook", + ":atomic_hook_test_helper", ":core_headers", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) @@ -231,19 +397,49 @@ cc_test( deps = [ ":base", ":core_headers", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "attributes_test", + srcs = [ + "attributes_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":core_headers", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "c_header_test", + srcs = ["c_header_test.c"], + tags = [ + "no_test_wasm", + ], + deps = [ + ":config", + ":core_headers", ], ) cc_library( name = "throw_delegate", - srcs = ["internal/throw_delegate.cc"], - hdrs = ["internal/throw_delegate.h"], + srcs = [ + "throw_delegate.cc", + ], + hdrs = [ + "throw_delegate.h", + ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl:__subpackages__", - ], + visibility = ["//visibility:public"], deps = [ ":config", ":raw_logging_internal", @@ -258,22 +454,38 @@ cc_test( deps = [ ":config", ":throw_delegate", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "errno_saver_test", + size = "small", + srcs = ["internal/errno_saver_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":errno_saver", + ":strerror", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) cc_library( name = "exception_testing", - testonly = 1, + testonly = True, hdrs = ["internal/exception_testing.h"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/util/gtl:__subpackages__", ], deps = [ ":config", - "@com_google_googletest//:gtest", + "@googletest//:gtest", ], ) @@ -281,24 +493,28 @@ cc_library( name = "pretty_function", hdrs = ["internal/pretty_function.h"], linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//absl:__subpackages__"], + visibility = [ + "//absl:__subpackages__", + ], ) cc_library( name = "exception_safety_testing", - testonly = 1, + testonly = True, srcs = ["internal/exception_safety_testing.cc"], hdrs = ["internal/exception_safety_testing.h"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//absl:__subpackages__"], deps = [ ":config", ":pretty_function", "//absl/memory", "//absl/meta:type_traits", "//absl/strings", + "//absl/strings:string_view", "//absl/utility", - "@com_google_googletest//:gtest", + "@googletest//:gtest", ], ) @@ -310,38 +526,8 @@ cc_test( deps = [ ":exception_safety_testing", "//absl/memory", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "inline_variable_test", - size = "small", - srcs = [ - "inline_variable_test.cc", - "inline_variable_test_a.cc", - "inline_variable_test_b.cc", - "internal/inline_variable_testing.h", - ], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - deps = [ - ":base_internal", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "invoke_test", - size = "small", - srcs = ["invoke_test.cc"], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - deps = [ - ":base_internal", - "//absl/memory", - "//absl/strings", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) @@ -349,16 +535,20 @@ cc_test( # AbslInternalSpinLockDelay and AbslInternalSpinLockWake. cc_library( name = "spinlock_test_common", - testonly = 1, + testonly = True, srcs = ["spinlock_test_common.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "@do_not_use_for_gloop_visibility_only//gloop/base:__pkg__", + ], deps = [ ":base", + ":base_internal", + ":config", ":core_headers", - ":spinlock_wait", "//absl/synchronization", - "@com_google_googletest//:gtest", + "@googletest//:gtest", ], alwayslink = 1, ) @@ -369,18 +559,23 @@ cc_test( srcs = ["spinlock_test_common.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":base", + ":base_internal", + ":config", ":core_headers", - ":spinlock_wait", "//absl/synchronization", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) cc_library( name = "spinlock_benchmark_common", - testonly = 1, + testonly = True, srcs = ["internal/spinlock_benchmark.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, @@ -390,18 +585,20 @@ cc_library( deps = [ ":base", ":base_internal", + ":no_destructor", ":raw_logging_internal", "//absl/synchronization", - "@com_github_google_benchmark//:benchmark_main", + "@google_benchmark//:benchmark_main", ], alwayslink = 1, ) cc_binary( name = "spinlock_benchmark", - testonly = 1, + testonly = True, copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], visibility = ["//visibility:private"], deps = [ ":spinlock_benchmark_common", @@ -416,9 +613,14 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], deps = [ + ":base", ":config", ":core_headers", + ":nullability", ], ) @@ -429,7 +631,8 @@ cc_test( deps = [ ":config", ":endian", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) @@ -441,7 +644,8 @@ cc_test( deps = [ ":config", "//absl/synchronization:thread_pool", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) @@ -454,7 +658,102 @@ cc_test( ":base", ":core_headers", "//absl/synchronization", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "casts_test", + size = "small", + srcs = [ + "casts_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":base", + ":config", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "hardening_test", + size = "small", + srcs = [ + "internal/hardening_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":core_headers", + ":hardening", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "no_destructor_test", + srcs = ["no_destructor_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":no_destructor", + ":raw_logging_internal", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_binary( + name = "no_destructor_benchmark", + testonly = True, + srcs = ["no_destructor_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":no_destructor", + ":raw_logging_internal", + "@google_benchmark//:benchmark_main", + ], +) + +cc_test( + name = "nullability_test", + srcs = ["nullability_test.cc"], + deps = [ + ":nullability", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "nullability_default_nonnull_test", + srcs = ["nullability_default_nonnull_test.cc"], + deps = [ + ":nullability", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "nullability_traits_test", + srcs = ["internal/nullability_traits_test.cc"], + deps = [ + ":config", + ":nullability", + ":nullability_traits_internal", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) @@ -466,7 +765,8 @@ cc_test( deps = [ ":raw_logging_internal", "//absl/strings", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) @@ -479,7 +779,22 @@ cc_test( deps = [ ":base", "//absl/synchronization", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "cpu_detect_test", + size = "small", + srcs = ["internal/cpu_detect_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":cpu_detect", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) @@ -489,26 +804,36 @@ cc_test( srcs = ["internal/low_level_alloc_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = ["no_test_ios_x86_64"], - deps = [":malloc_internal"], + tags = [ + "no_test_ios_x86_64", + "no_test_wasm", + ], + deps = [ + ":malloc_internal", + "//absl/container:node_hash_map", + ], ) cc_test( name = "thread_identity_test", - size = "small", srcs = ["internal/thread_identity_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":base", ":core_headers", "//absl/synchronization", - "@com_google_googletest//:gtest_main", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) -cc_test( +cc_binary( name = "thread_identity_benchmark", + testonly = True, srcs = ["internal/thread_identity_benchmark.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, @@ -517,66 +842,296 @@ cc_test( deps = [ ":base", "//absl/synchronization", - "@com_github_google_benchmark//:benchmark_main", + "@google_benchmark//:benchmark_main", ], ) cc_library( - name = "bits", - hdrs = ["internal/bits.h"], + name = "scoped_set_env", + testonly = True, + srcs = ["internal/scoped_set_env.cc"], + hdrs = ["internal/scoped_set_env.h"], + copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], - deps = [":core_headers"], + deps = [ + ":config", + ":raw_logging_internal", + ], ) cc_test( - name = "bits_test", + name = "scoped_set_env_test", size = "small", - srcs = ["internal/bits_test.cc"], + srcs = ["internal/scoped_set_env_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":bits", - "@com_google_googletest//:gtest_main", + ":scoped_set_env", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "log_severity_test", + size = "small", + srcs = ["log_severity_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":log_severity", + "//absl/flags:flag_internal", + "//absl/flags:marshalling", + "//absl/strings", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) cc_library( - name = "scoped_set_env", - testonly = 1, - srcs = ["internal/scoped_set_env.cc"], - hdrs = ["internal/scoped_set_env.h"], + name = "strerror", + srcs = ["internal/strerror.cc"], + hdrs = ["internal/strerror.h"], + copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/base:__subpackages__", + ], + deps = [ + ":config", + ":core_headers", + ":errno_saver", ], - deps = [":raw_logging_internal"], ) cc_test( - name = "scoped_set_env_test", + name = "strerror_test", size = "small", - srcs = ["internal/scoped_set_env_test.cc"], + srcs = ["internal/strerror_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":scoped_set_env", - "@com_google_googletest//:gtest_main", + ":strerror", + "//absl/strings", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_binary( + name = "strerror_benchmark", + testonly = True, + srcs = ["internal/strerror_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":strerror", + "@google_benchmark//:benchmark_main", + ], +) + +cc_library( + name = "fast_type_id", + hdrs = ["fast_type_id.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], + deps = [ + ":config", ], ) cc_test( - name = "log_severity_test", + name = "fast_type_id_test", size = "small", - srcs = ["log_severity_test.cc"], + srcs = ["fast_type_id_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":log_severity", - "//absl/flags:marshalling", - "//absl/strings", - "@com_google_googletest//:gtest_main", + ":core_headers", + ":fast_type_id", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_library( + name = "prefetch", + hdrs = [ + "prefetch.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:public"], + deps = [ + ":config", + ":core_headers", + ], +) + +cc_test( + name = "prefetch_test", + size = "small", + srcs = [ + "prefetch_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":prefetch", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_library( + name = "poison", + srcs = [ + "internal/poison.cc", + ], + hdrs = ["internal/poison.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":config", + ":core_headers", + ":malloc_internal", + ], +) + +cc_test( + name = "poison_test", + size = "small", + timeout = "short", + srcs = [ + "internal/poison_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":poison", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "unique_small_name_test", + size = "small", + srcs = ["internal/unique_small_name_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + linkstatic = 1, + deps = [ + ":core_headers", + "//absl/strings:string_view", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "optimization_test", + size = "small", + srcs = ["optimization_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":core_headers", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_library( + name = "iterator_traits_internal", + hdrs = ["internal/iterator_traits.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/util/gtl:__subpackages__", + ], + deps = [ + ":config", + "//absl/meta:type_traits", + ], +) + +cc_test( + name = "iterator_traits_test", + srcs = ["internal/iterator_traits_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":iterator_traits_internal", + ":iterator_traits_test_helper", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_library( + name = "tracing_internal", + srcs = ["internal/tracing.cc"], + hdrs = ["internal/tracing.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + "@do_not_use_for_gloop_visibility_only//gloop/perftools/tracing:__subpackages__", + ], + deps = [ + ":config", + ":core_headers", + ], +) + +cc_library( + name = "iterator_traits_test_helper", + hdrs = ["internal/iterator_traits_test_helper.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + "//cloud/blockstore/metallica/util:__pkg__", + ], + deps = [":config"], +) + +cc_test( + name = "tracing_internal_weak_test", + srcs = ["internal/tracing_weak_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":tracing_internal", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "tracing_internal_strong_test", + srcs = ["internal/tracing_strong_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":core_headers", + ":tracing_internal", + "@googletest//:gtest", + "@googletest//:gtest_main", ], ) diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 2f11ef86556..20c09ef982d 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -14,11 +14,56 @@ # limitations under the License. # +find_library(LIBRT rt) + +# Internal-only target, do not depend on directly. absl_cc_library( NAME atomic_hook HDRS "internal/atomic_hook.h" + DEPS + absl::config + absl::core_headers + COPTS + ${ABSL_DEFAULT_COPTS} +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + base_cpu_detect + HDRS + "internal/cpu_detect.h" + SRCS + "internal/cpu_detect.cc" + DEPS + absl::base + absl::config + COPTS + ${ABSL_DEFAULT_COPTS} +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + errno_saver + HDRS + "internal/errno_saver.h" + DEPS + absl::config + COPTS + ${ABSL_DEFAULT_COPTS} +) + +absl_cc_library( + NAME + hardening + HDRS + "internal/hardening.h" + DEPS + absl::config + absl::core_headers COPTS ${ABSL_DEFAULT_COPTS} ) @@ -31,11 +76,88 @@ absl_cc_library( SRCS "log_severity.cc" DEPS + absl::config absl::core_headers COPTS ${ABSL_DEFAULT_COPTS} ) +absl_cc_library( + NAME + no_destructor + HDRS + "no_destructor.h" + DEPS + absl::config + absl::nullability + COPTS + ${ABSL_DEFAULT_COPTS} +) + +absl_cc_library( + NAME + nullability + HDRS + "nullability.h" + DEPS + absl::config + COPTS + ${ABSL_DEFAULT_COPTS} +) + +absl_cc_test( + NAME + nullability_test + SRCS + "nullability_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::nullability + GTest::gtest_main +) + +absl_cc_test( + NAME + nullability_default_nonnull_test + SRCS + "nullability_default_nonnull_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::nullability + GTest::gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + nullability_traits_internal + HDRS + "internal/nullability_traits.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::nullability + PUBLIC +) + +absl_cc_test( + NAME + nullability_traits_test + SRCS + "internal/nullability_traits_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::nullability + absl::nullability_traits_internal + GTest::gtest_main +) + +# Internal-only target, do not depend on directly. absl_cc_library( NAME raw_logging_internal @@ -47,16 +169,17 @@ absl_cc_library( absl::atomic_hook absl::config absl::core_headers + absl::errno_saver absl::log_severity COPTS ${ABSL_DEFAULT_COPTS} ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME spinlock_wait HDRS - "internal/scheduling_mode.h" "internal/spinlock_wait.h" SRCS "internal/spinlock_akaros.inc" @@ -67,7 +190,9 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base_internal absl::core_headers + absl::errno_saver ) absl_cc_library( @@ -75,6 +200,7 @@ absl_cc_library( config HDRS "config.h" + "options.h" "policy_checks.h" COPTS ${ABSL_DEFAULT_COPTS} @@ -87,11 +213,11 @@ absl_cc_library( HDRS "dynamic_annotations.h" SRCS - "dynamic_annotations.cc" + "internal/dynamic_annotations.h" COPTS ${ABSL_DEFAULT_COPTS} - DEFINES - "__CLANG_SUPPORT_DYN_ANNOTATION__" + DEPS + absl::config PUBLIC ) @@ -105,7 +231,6 @@ absl_cc_library( "optimization.h" "port.h" "thread_annotations.h" - "internal/thread_annotations.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -113,6 +238,7 @@ absl_cc_library( PUBLIC ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME malloc_internal @@ -125,25 +251,25 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::base + absl::base_internal absl::config absl::core_headers absl::dynamic_annotations absl::raw_logging_internal - absl::spinlock_wait Threads::Threads ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME base_internal HDRS "internal/hide_ptr.h" - "internal/identity.h" - "internal/inline_variable.h" - "internal/invoke.h" + "internal/scheduling_mode.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config absl::type_traits ) @@ -154,6 +280,7 @@ absl_cc_library( "call_once.h" "casts.h" "internal/cycleclock.h" + "internal/cycleclock_config.h" "internal/low_level_scheduling.h" "internal/per_thread_tls.h" "internal/spinlock.h" @@ -161,16 +288,20 @@ absl_cc_library( "internal/thread_identity.h" "internal/tsan_mutex_interface.h" "internal/unscaledcycleclock.h" - "log_severity.h" + "internal/unscaledcycleclock_config.h" SRCS + "casts.cc" "internal/cycleclock.cc" "internal/spinlock.cc" "internal/sysinfo.cc" "internal/thread_identity.cc" "internal/unscaledcycleclock.cc" - "log_severity.cc" COPTS ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + $<$:-lrt> + $<$:-ladvapi32> DEPS absl::atomic_hook absl::base_internal @@ -178,6 +309,7 @@ absl_cc_library( absl::core_headers absl::dynamic_annotations absl::log_severity + absl::nullability absl::raw_logging_internal absl::spinlock_wait absl::type_traits @@ -189,9 +321,9 @@ absl_cc_library( NAME throw_delegate HDRS - "internal/throw_delegate.h" + "throw_delegate.h" SRCS - "internal/throw_delegate.cc" + "throw_delegate.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -199,6 +331,7 @@ absl_cc_library( absl::raw_logging_internal ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME exception_testing @@ -208,10 +341,11 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::config - gtest + GTest::gtest TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME pretty_function @@ -221,6 +355,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME exception_safety_testing @@ -235,9 +370,10 @@ absl_cc_library( absl::pretty_function absl::memory absl::meta + absl::string_view absl::strings absl::utility - gtest + GTest::gtest TESTONLY ) @@ -251,7 +387,21 @@ absl_cc_test( DEPS absl::exception_safety_testing absl::memory - gtest_main + GTest::gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + atomic_hook_test_helper + SRCS + "internal/atomic_hook_test_helper.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::atomic_hook + absl::core_headers + TESTONLY ) absl_cc_test( @@ -262,9 +412,38 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::atomic_hook_test_helper absl::atomic_hook absl::core_headers - gtest_main + GTest::gmock + GTest::gtest_main +) + +absl_cc_test( + NAME + attributes_test + SRCS + "attributes_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::core_headers + GTest::gtest_main +) + +absl_cc_test( + NAME + hardening_test + SRCS + "internal/hardening_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::core_headers + absl::hardening + GTest::gtest_main ) absl_cc_test( @@ -277,53 +456,51 @@ absl_cc_test( DEPS absl::base absl::core_headers - gtest_main + GTest::gtest_main ) absl_cc_test( NAME - throw_delegate_test + casts_test SRCS - "throw_delegate_test.cc" + "casts_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS absl::base - absl::config - absl::throw_delegate - gtest_main + absl::core_headers + GTest::gtest_main ) absl_cc_test( NAME - inline_variable_test + errno_saver_test SRCS - "internal/inline_variable_testing.h" - "inline_variable_test.cc" - "inline_variable_test_a.cc" - "inline_variable_test_b.cc" + "internal/errno_saver_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS - absl::base_internal - gtest_main + absl::errno_saver + absl::strerror + GTest::gmock + GTest::gtest_main ) absl_cc_test( NAME - invoke_test + throw_delegate_test SRCS - "invoke_test.cc" + "throw_delegate_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS - absl::base_internal - absl::memory - absl::strings - gmock - gtest_main + absl::base + absl::config + absl::throw_delegate + GTest::gtest_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME spinlock_test_common @@ -333,10 +510,11 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::base + absl::config + absl::base_internal absl::core_headers - absl::spinlock_wait absl::synchronization - gtest + GTest::gtest TESTONLY ) @@ -350,12 +528,14 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::base + absl::base_internal + absl::config absl::core_headers - absl::spinlock_wait absl::synchronization - gtest_main + GTest::gtest_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME endian @@ -365,8 +545,10 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base absl::config absl::core_headers + absl::nullability PUBLIC ) @@ -381,7 +563,7 @@ absl_cc_test( absl::base absl::config absl::endian - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -394,7 +576,7 @@ absl_cc_test( DEPS absl::config absl::synchronization - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -408,7 +590,22 @@ absl_cc_test( absl::base absl::core_headers absl::synchronization - gtest_main + GTest::gtest_main +) + +absl_cc_test( + NAME + no_destructor_test + SRCS + "no_destructor_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::no_destructor + absl::config + absl::raw_logging_internal + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -421,7 +618,7 @@ absl_cc_test( DEPS absl::raw_logging_internal absl::strings - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -434,7 +631,20 @@ absl_cc_test( DEPS absl::base absl::synchronization - gtest_main + GTest::gtest_main +) + +absl_cc_test( + NAME + cpu_detect_test + SRCS + "internal/cpu_detect_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base_cpu_detect + absl::config + GTest::gtest_main ) absl_cc_test( @@ -446,6 +656,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::malloc_internal + absl::node_hash_map Threads::Threads ) @@ -461,77 +672,263 @@ absl_cc_test( absl::core_headers absl::synchronization Threads::Threads - gtest_main + GTest::gtest_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME - bits + scoped_set_env + SRCS + "internal/scoped_set_env.cc" HDRS - "internal/bits.h" + "internal/scoped_set_env.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config + absl::raw_logging_internal +) + +absl_cc_test( + NAME + scoped_set_env_test + SRCS + "internal/scoped_set_env_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::scoped_set_env + GTest::gtest_main +) + +absl_cc_test( + NAME + cmake_thread_test + SRCS + "internal/cmake_thread_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base +) + +absl_cc_test( + NAME + log_severity_test + SRCS + "log_severity_test.cc" + DEPS + absl::flags_internal + absl::flags_marshalling + absl::log_severity + absl::strings + GTest::gmock + GTest::gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + strerror + SRCS + "internal/strerror.cc" + HDRS + "internal/strerror.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config absl::core_headers + absl::errno_saver ) absl_cc_test( NAME - bits_test + strerror_test SRCS - "internal/bits_test.cc" + "internal/strerror_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS - absl::bits - gtest_main + absl::strerror + absl::strings + GTest::gmock + GTest::gtest_main ) absl_cc_library( NAME - scoped_set_env + fast_type_id + HDRS + "fast_type_id.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config +) + +absl_cc_test( + NAME + fast_type_id_test SRCS - "internal/scoped_set_env.cc" + "fast_type_id_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::core_headers + absl::fast_type_id + GTest::gtest_main +) + +absl_cc_library( + NAME + prefetch HDRS - "internal/scoped_set_env.h" + "prefetch.h" COPTS ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::raw_logging_internal + absl::config + absl::core_headers ) absl_cc_test( NAME - scoped_set_env_test + prefetch_test SRCS - "internal/scoped_set_env_test.cc" + "prefetch_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS - absl::scoped_set_env - gtest_main + absl::prefetch + GTest::gtest_main ) absl_cc_test( NAME - cmake_thread_test + optimization_test SRCS - "internal/cmake_thread_test.cc" + "optimization_test.cc" COPTS ${ABSL_TEST_COPTS} + DEPS + absl::core_headers + GTest::gtest_main +) + +absl_cc_library( + NAME + poison + SRCS + "internal/poison.cc" + HDRS + "internal/poison.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config + absl::core_headers + absl::malloc_internal +) + +absl_cc_test( + NAME + poison_test + SRCS + "internal/poison_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::poison + GTest::gtest_main +) + +absl_cc_library( + NAME + tracing_internal + HDRS + "internal/tracing.h" + SRCS + "internal/tracing.cc" + COPTS + ${ABSL_DEFAULT_COPTS} DEPS absl::base ) absl_cc_test( NAME - log_severity_test + tracing_internal_weak_test SRCS - "log_severity_test.cc" + "internal/tracing_weak_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS - absl::flags_marshalling - absl::log_severity - absl::strings - gmock - gtest_main + absl::base + absl::tracing_internal + GTest::gtest_main +) + +absl_cc_test( + NAME + tracing_internal_strong_test + SRCS + "internal/tracing_strong_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::tracing_internal + GTest::gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + iterator_traits_internal + HDRS + "internal/iterator_traits.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::type_traits + PUBLIC +) + +absl_cc_test( + NAME + iterator_traits_test + SRCS + "internal/iterator_traits_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::iterator_traits_internal + absl::iterator_traits_test_helper_internal + GTest::gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + iterator_traits_test_helper_internal + HDRS + "internal/iterator_traits_test_helper.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + PUBLIC ) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index f298297e994..53e440e68fe 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -18,8 +18,6 @@ // These macros are used within Abseil and allow the compiler to optimize, where // applicable, certain function calls. // -// This file is used for both C and C++! -// // Most macros here are exposing GCC or Clang features, and are stubbed out for // other compilers. // @@ -32,34 +30,14 @@ // of them are not supported in older version of Clang. Thus, we check // `__has_attribute()` first. If the check fails, we check if we are on GCC and // assume the attribute exists on GCC (which is verified on GCC 4.7). -// -// ----------------------------------------------------------------------------- -// Sanitizer Attributes -// ----------------------------------------------------------------------------- -// -// Sanitizer-related attributes are not "defined" in this file (and indeed -// are not defined as such in any file). To utilize the following -// sanitizer-related attributes within your builds, define the following macros -// within your build using a `-D` flag, along with the given value for -// `-fsanitize`: -// -// * `ADDRESS_SANITIZER` + `-fsanitize=address` (Clang, GCC 4.8) -// * `MEMORY_SANITIZER` + `-fsanitize=memory` (Clang-only) -// * `THREAD_SANITIZER + `-fsanitize=thread` (Clang, GCC 4.8+) -// * `UNDEFINED_BEHAVIOR_SANITIZER` + `-fsanitize=undefined` (Clang, GCC 4.9+) -// * `CONTROL_FLOW_INTEGRITY` + -fsanitize=cfi (Clang-only) -// -// Example: -// -// // Enable branches in the Abseil code that are tagged for ASan: -// $ bazel build --copt=-DADDRESS_SANITIZER --copt=-fsanitize=address -// --linkopt=-fsanitize=address *target* -// -// Since these macro names are only supported by GCC and Clang, we only check -// for `__GNUC__` (GCC or Clang) and the above macros. + +// SKIP_ABSL_INLINE_NAMESPACE_CHECK + #ifndef ABSL_BASE_ATTRIBUTES_H_ #define ABSL_BASE_ATTRIBUTES_H_ +#include "absl/base/config.h" + // ABSL_HAVE_ATTRIBUTE // // A function-like feature checking macro that is a wrapper around @@ -143,7 +121,7 @@ #if ABSL_HAVE_ATTRIBUTE(disable_tail_calls) #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 #define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls)) -#elif defined(__GNUC__) && !defined(__clang__) +#elif defined(__GNUC__) && !defined(__clang__) && !defined(__e2k__) #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 #define ABSL_ATTRIBUTE_NO_TAIL_CALL \ __attribute__((optimize("no-optimize-sibling-calls"))) @@ -155,12 +133,17 @@ // ABSL_ATTRIBUTE_WEAK // // Tags a function as weak for the purposes of compilation and linking. -// Weak attributes currently do not work properly in LLVM's Windows backend, -// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 -// for further information. -#if (ABSL_HAVE_ATTRIBUTE(weak) || \ - (defined(__GNUC__) && !defined(__clang__))) && \ - !(defined(__llvm__) && defined(_WIN32)) +// Weak attributes did not work properly in LLVM's Windows backend before +// 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 +// for further information. Weak attributes do not work across DLL boundary. +// The MinGW compiler doesn't complain about the weak attribute until the link +// step, presumably because Windows doesn't use ELF binaries. +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + (!defined(_WIN32) || \ + (defined(__clang__) && __clang_major__ >= 9 && \ + !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL))) && \ + !defined(__MINGW32__) #undef ABSL_ATTRIBUTE_WEAK #define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) #define ABSL_HAVE_ATTRIBUTE_WEAK 1 @@ -216,6 +199,9 @@ // ABSL_ATTRIBUTE_NORETURN // // Tells the compiler that a given function never returns. +// +// Deprecated: Prefer the `[[noreturn]]` attribute standardized by C++11 over +// this macro. #if ABSL_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__)) #define ABSL_ATTRIBUTE_NORETURN __attribute__((noreturn)) #elif defined(_MSC_VER) @@ -232,21 +218,33 @@ // out of bounds or does other scary things with memory. // NOTE: GCC supports AddressSanitizer(asan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if defined(__GNUC__) +#if defined(ABSL_HAVE_ADDRESS_SANITIZER) && \ + ABSL_HAVE_ATTRIBUTE(no_sanitize_address) #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) && defined(_MSC_VER) && \ + _MSC_VER >= 1928 +// https://docs.microsoft.com/en-us/cpp/cpp/no-sanitize-address +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __declspec(no_sanitize_address) +#elif defined(ABSL_HAVE_HWADDRESS_SANITIZER) && ABSL_HAVE_ATTRIBUTE(no_sanitize) +// HWAddressSanitizer is a sanitizer similar to AddressSanitizer, which uses CPU +// features to detect similar bugs with less CPU and memory overhead. +// NOTE: GCC supports HWAddressSanitizer(hwasan) since 11. +// https://gcc.gnu.org/gcc-11/changes.html +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS \ + __attribute__((no_sanitize("hwaddress"))) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS #endif // ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // -// Tells the MemorySanitizer to relax the handling of a given function. All -// "Use of uninitialized value" warnings from such functions will be suppressed, -// and all values loaded from memory will be considered fully initialized. -// This attribute is similar to the ADDRESS_SANITIZER attribute above, but deals -// with initialized-ness rather than addressability issues. +// Tells the MemorySanitizer to relax the handling of a given function. All "Use +// of uninitialized value" warnings from such functions will be suppressed, and +// all values loaded from memory will be considered fully initialized. This +// attribute is similar to the ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS attribute +// above, but deals with initialized-ness rather than addressability issues. // NOTE: MemorySanitizer(msan) is supported by Clang but not GCC. -#if defined(__clang__) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_memory) #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY @@ -257,7 +255,7 @@ // Tells the ThreadSanitizer to not instrument a given function. // NOTE: GCC supports ThreadSanitizer(tsan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if defined(__GNUC__) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_thread) #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD @@ -269,8 +267,10 @@ // where certain behavior (eg. division by zero) is being used intentionally. // NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9. // https://gcc.gnu.org/gcc-4.9/changes.html -#if defined(__GNUC__) && \ - (defined(UNDEFINED_BEHAVIOR_SANITIZER) || defined(ADDRESS_SANITIZER)) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_undefined) +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ + __attribute__((no_sanitize_undefined)) +#elif ABSL_HAVE_ATTRIBUTE(no_sanitize) #define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ __attribute__((no_sanitize("undefined"))) #else @@ -281,7 +281,7 @@ // // Tells the ControlFlowIntegrity sanitizer to not instrument a given function. // See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details. -#if defined(__GNUC__) && defined(CONTROL_FLOW_INTEGRITY) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) && defined(__llvm__) #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi"))) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI @@ -291,7 +291,7 @@ // // Tells the SafeStack to not instrument a given function. // See https://clang.llvm.org/docs/SafeStack.html for details. -#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) #define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \ __attribute__((no_sanitize("safe-stack"))) #else @@ -301,10 +301,7 @@ // ABSL_ATTRIBUTE_RETURNS_NONNULL // // Tells the compiler that a particular function never returns a null pointer. -#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) || \ - (defined(__GNUC__) && \ - (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \ - !defined(__clang__)) +#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) #define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) #else #define ABSL_ATTRIBUTE_RETURNS_NONNULL @@ -334,15 +331,22 @@ __attribute__((section(#name))) __attribute__((noinline)) #endif - // ABSL_ATTRIBUTE_SECTION_VARIABLE // // Tells the compiler/linker to put a given variable into a section and define // `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. // This functionality is supported by GNU linker. #ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE +#ifdef _AIX +// __attribute__((section(#name))) on AIX is achieved by using the `.csect` +// pseudo op which includes an additional integer as part of its syntax +// indicating alignment. If data fall under different alignments then you might +// get a compilation error indicating a `Section type conflict`. +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) +#else #define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name))) #endif +#endif // ABSL_DECLARE_ATTRIBUTE_SECTION_VARS // @@ -353,8 +357,8 @@ // a no-op on ELF but not on Mach-O. // #ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS -#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ - extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ +#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ + extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK #endif #ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS @@ -415,6 +419,9 @@ // // Tells the compiler to warn about unused results. // +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard `[[nodiscard]]` directly over this macro. +// // When annotating a function, it must appear as the first part of the // declaration or definition. The compiler will warn if the return value from // such a function is unused: @@ -441,9 +448,10 @@ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 // // Note: past advice was to place the macro after the argument list. -#if ABSL_HAVE_ATTRIBUTE(nodiscard) -#define ABSL_MUST_USE_RESULT [[nodiscard]] -#elif defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) +// +// TODO(b/176172494): Use ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) when all code is +// compliant with the stricter [[nodiscard]]. +#if defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) #define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result)) #else #define ABSL_MUST_USE_RESULT @@ -486,8 +494,8 @@ // // These attributes only take effect when the following conditions are met: // -// * The file/target is built in at least C++11 mode, with a Clang compiler -// that supports XRay attributes. +// * The file/target is built with a Clang compiler that supports XRay +// attributes. // * The file/target is built with the -fxray-instrument flag set for the // Clang/LLVM compiler. // * The function is defined in the translation unit (the compiler honors the @@ -505,13 +513,15 @@ // packages/targets, as this may lead to conflicting definitions of functions at // link-time. // +// XRay isn't currently supported on Android: +// https://github.com/android/ndk/issues/368 #if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && \ - !defined(ABSL_NO_XRAY_ATTRIBUTES) + !defined(ABSL_NO_XRAY_ATTRIBUTES) && !defined(__ANDROID__) #define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]] #define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]] #if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args) #define ABSL_XRAY_LOG_ARGS(N) \ - [[clang::xray_always_instrument, clang::xray_log_args(N)]] + [[clang::xray_always_instrument, clang::xray_log_args(N)]] #else #define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]] #endif @@ -521,6 +531,25 @@ #define ABSL_XRAY_LOG_ARGS(N) #endif +// ABSL_ATTRIBUTE_NULL_AFTER_MOVE +// +// Indicates that a user-defined smart-pointer-like type makes guarantees on the +// state of a moved-from object, leaving it in a null state, where it can be +// used as long as it is not dereferenced. In other words, these are the same +// semantics that smart pointers from the standard library provide. +// +// The clang-tidy check bugprone-use-after-move allows member functions of types +// marked with this attribute to be called on objects that have been moved from; +// without the attribute, this would result in a use-after-move warning. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::annotate) && defined(__clang__) && \ + __clang_major__ >= 12 +#define ABSL_ATTRIBUTE_NULL_AFTER_MOVE \ + [[clang::annotate("clang-tidy", "bugprone-use-after-move", \ + "null_after_move")]] +#else +#define ABSL_ATTRIBUTE_NULL_AFTER_MOVE +#endif + // ABSL_ATTRIBUTE_REINITIALIZES // // Indicates that a member function reinitializes the entire object to a known @@ -542,6 +571,12 @@ // ABSL_ATTRIBUTE_UNUSED // // Prevents the compiler from complaining about variables that appear unused. +// +// Deprecated: Use the standard C++17 `[[maybe_unused]]` instead. +// +// Due to differences in positioning requirements between the old, compiler +// specific __attribute__ syntax and the now standard `[[maybe_unused]]`, this +// macro does not attempt to take advantage of `[[maybe_unused]]`. #if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__)) #undef ABSL_ATTRIBUTE_UNUSED #define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__)) @@ -561,7 +596,29 @@ // ABSL_ATTRIBUTE_PACKED // -// Prevents the compiler from padding a structure to natural alignment +// Instructs the compiler not to use natural alignment for a tagged data +// structure, but instead to reduce its alignment to 1. +// +// Use of this attribute is HIGHLY DISCOURAGED. Taking the address of or +// binding a reference to any unaligned member is UB, and it is very easy to +// do so unintentionally when passing such members as function arguments. +// +// DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing +// so can cause atomic variables to be mis-aligned and silently violate +// atomicity on x86. +// +// This attribute can either be applied to members of a structure or to a +// structure in its entirety. Applying this attribute (judiciously) to a +// structure in its entirety to optimize the memory footprint of very +// commonly-used structs is fine. Do not apply this attribute to a structure in +// its entirety if the purpose is to control the offsets of the members in the +// structure. Instead, apply this attribute only to structure members that need +// it. +// +// When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the +// natural alignment of structure members not annotated is preserved. Aligned +// member accesses are faster than non-aligned member accesses even if the +// targeted microprocessor supports non-aligned accesses. #if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__)) #define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__)) #else @@ -578,15 +635,167 @@ #define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) #endif +// ABSL_FALLTHROUGH_INTENDED +// +// Annotates implicit fall-through between switch labels, allowing a case to +// indicate intentional fallthrough and turn off warnings about any lack of a +// `break` statement. +// +// Deprecated: Use the standard C++17 `[[fallthrough]]` instead. +// +// This macro has no effect on runtime behavior and performance of code. + +#ifdef ABSL_FALLTHROUGH_INTENDED +#error "ABSL_FALLTHROUGH_INTENDED should not be defined." +#endif +#define ABSL_FALLTHROUGH_INTENDED [[fallthrough]] + +// ABSL_DEPRECATED() +// +// Marks a deprecated class, struct, enum, function, method and variable +// declarations. The macro argument is used as a custom diagnostic message (e.g. +// suggestion of a better alternative). +// +// For code or headers that are assured to only build with C++14 and up, prefer +// just using the standard `[[deprecated("message")]]` directly over this macro. +// +// Examples: +// +// class ABSL_DEPRECATED("Use Bar instead") Foo {...}; +// +// ABSL_DEPRECATED("Use Baz() instead") void Bar() {...} +// +// template +// ABSL_DEPRECATED("Use DoThat() instead") +// void DoThis(); +// +// enum FooEnum { +// kBar ABSL_DEPRECATED("Use kBaz instead"), +// }; +// +// Every usage of a deprecated entity will trigger a warning when compiled with +// GCC/Clang's `-Wdeprecated-declarations` option. Google's production toolchain +// turns this warning off by default, instead relying on clang-tidy to report +// new uses of deprecated code. +#if ABSL_HAVE_ATTRIBUTE(deprecated) +#define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) +#else +#define ABSL_DEPRECATED(message) +#endif + +// When deprecating Abseil code, it is sometimes necessary to turn off the +// warning within Abseil, until the deprecated code is actually removed. The +// deprecated code can be surrounded with these directives to achieve that +// result. +// +// class ABSL_DEPRECATED("Use Bar instead") Foo; +// +// ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING +// Baz ComputeBazFromFoo(Foo f); +// ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING +#if defined(__GNUC__) || defined(__clang__) +// Clang also supports these GCC pragmas. +#define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING \ + _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +#define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING \ + _Pragma("warning(push)") _Pragma("warning(disable: 4996)") +#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING \ + _Pragma("warning(pop)") +#else +#define ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING +#define ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING +#endif // defined(__GNUC__) || defined(__clang__) + +// ABSL_REQUIRE_EXPLICIT_INIT +// +// ABSL_REQUIRE_EXPLICIT_INIT is placed *after* the data members of an aggregate +// type to indicate that the annotated member must be explicitly initialized by +// the user whenever the aggregate is constructed. For example: +// +// struct Coord { +// int x ABSL_REQUIRE_EXPLICIT_INIT; +// int y ABSL_REQUIRE_EXPLICIT_INIT; +// }; +// Coord coord = {1}; // warning: field 'y' is not explicitly initialized +// +// Note that usage on C arrays is not supported in C++. +// Use a struct (such as std::array) to wrap the array member instead. +// +// Avoid applying this attribute to the members of non-aggregate types. +// The behavior within non-aggregates is unspecified and subject to change. +// +// Do NOT attempt to suppress or demote the error generated by this attribute. +// Just like with a missing function argument, it is a hard error by design. +// +// See the upstream documentation for more details: +// https://clang.llvm.org/docs/AttributeReference.html#require-explicit-initialization +#ifdef __cplusplus +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_explicit_initialization) +// clang-format off +#define ABSL_REQUIRE_EXPLICIT_INIT \ + [[clang::require_explicit_initialization]] = \ + AbslInternal_YouForgotToExplicitlyInitializeAField::v +#else +#define ABSL_REQUIRE_EXPLICIT_INIT \ + = AbslInternal_YouForgotToExplicitlyInitializeAField::v +#endif +// clang-format on +#else +// clang-format off +#if ABSL_HAVE_ATTRIBUTE(require_explicit_initialization) +#define ABSL_REQUIRE_EXPLICIT_INIT \ + __attribute__((require_explicit_initialization)) +#else +#define ABSL_REQUIRE_EXPLICIT_INIT \ + /* No portable fallback for C is available */ +#endif +// clang-format on +#endif + +#ifdef __cplusplus +struct AbslInternal_YouForgotToExplicitlyInitializeAField { + // A portable version of [[clang::require_explicit_initialization]] that + // never builds, as a last resort for all toolchains. + // The error messages are poor, so we don't rely on this unless we have to. + template +#if !defined(SWIG) + constexpr +#endif + operator T() const /* NOLINT */ { + const void *volatile deliberately_volatile_ptr = nullptr; + // Infinite loop to prevent constexpr compilation + for (;;) { + // This assignment ensures the 'this' pointer is not optimized away, so + // that linking always fails. + deliberately_volatile_ptr = this; // Deliberately not constexpr + (void)deliberately_volatile_ptr; + } + } + // This is deliberately left undefined to prevent linking + static AbslInternal_YouForgotToExplicitlyInitializeAField v; +}; +#endif + // ABSL_CONST_INIT // -// A variable declaration annotated with the `ABSL_CONST_INIT` attribute will -// not compile (on supported platforms) unless the variable has a constant -// initializer. This is useful for variables with static and thread storage -// duration, because it guarantees that they will not suffer from the so-called -// "static init order fiasco". Prefer to put this attribute on the most visible -// declaration of the variable, if there's more than one, because code that -// accesses the variable can then use the attribute for optimization. +// A variable declared with `ABSL_CONST_INIT` will not compile (on supported +// platforms) unless the variable has a constant initializer. This is useful for +// variables with static and thread storage duration, because it guarantees that +// they will not suffer from the so-called "static init order fiasco". +// +// `ABSL_CONST_INIT` must be placed on the initializing declaration +// (i.e. definition) of the variable. Some compilers will give a +// `-Wmissing-constinit` warning when it is placed on some other +// declaration but missing from the initializing declaration. +// +// For thread_local variables, placing `ABSL_CONST_INIT` on the non-initializing +// declaration tells the compiler that the variable is already initialized, +// reducing overhead that would otherwise be incurred by a hidden guard +// variable. // // Example: // @@ -595,14 +804,282 @@ // ABSL_CONST_INIT static MyType my_var; // }; // -// MyType MyClass::my_var = MakeMyType(...); +// ABSL_CONST_INIT MyType MyClass::my_var = MakeMyType(...); // -// Note that this attribute is redundant if the variable is declared constexpr. -#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) -// NOLINTNEXTLINE(whitespace/braces) +// For code or headers that are assured to only build with C++20 and up, prefer +// using the standard `constinit` keyword directly over this macro. +// +// Note that `ABSL_CONST_INIT` must not be used on a variable declared +// constexpr. +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +#define ABSL_CONST_INIT constinit +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) #define ABSL_CONST_INIT [[clang::require_constant_initialization]] #else #define ABSL_CONST_INIT -#endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#endif + +// ABSL_ATTRIBUTE_PURE_FUNCTION +// +// ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure" +// functions. A function is pure if its return value is only a function of its +// arguments. The pure attribute prohibits a function from modifying the state +// of the program that is observable by means other than inspecting the +// function's return value. Declaring such functions with the pure attribute +// allows the compiler to avoid emitting some calls in repeated invocations of +// the function with the same argument values. +// +// Example: +// +// ABSL_ATTRIBUTE_PURE_FUNCTION std::string FormatTime(Time t); +#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::pure) +#define ABSL_ATTRIBUTE_PURE_FUNCTION [[gnu::pure]] +#elif ABSL_HAVE_ATTRIBUTE(pure) +#define ABSL_ATTRIBUTE_PURE_FUNCTION __attribute__((pure)) +#else +// If the attribute isn't defined, we'll fallback to ABSL_MUST_USE_RESULT since +// pure functions are useless if its return is ignored. +#define ABSL_ATTRIBUTE_PURE_FUNCTION ABSL_MUST_USE_RESULT +#endif + +// ABSL_ATTRIBUTE_CONST_FUNCTION +// +// ABSL_ATTRIBUTE_CONST_FUNCTION is used to annotate declarations of "const" +// functions. A const function is similar to a pure function, with one +// exception: Pure functions may return value that depend on a non-volatile +// object that isn't provided as a function argument, while the const function +// is guaranteed to return the same result given the same arguments. +// +// Example: +// +// ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Milliseconds(Duration d); +#if defined(_MSC_VER) && !defined(__clang__) +// Put the MSVC case first since MSVC seems to parse const as a C++ keyword. +#define ABSL_ATTRIBUTE_CONST_FUNCTION ABSL_ATTRIBUTE_PURE_FUNCTION +#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::const) +#define ABSL_ATTRIBUTE_CONST_FUNCTION [[gnu::const]] +#elif ABSL_HAVE_ATTRIBUTE(const) +#define ABSL_ATTRIBUTE_CONST_FUNCTION __attribute__((const)) +#else +// Since const functions are more restrictive pure function, we'll fallback to a +// pure function if the const attribute is not handled. +#define ABSL_ATTRIBUTE_CONST_FUNCTION ABSL_ATTRIBUTE_PURE_FUNCTION +#endif + +// ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function +// parameter or implicit object parameter is retained by the return value of the +// annotated function (or, for a parameter of a constructor, in the value of the +// constructed object). This attribute causes warnings to be produced if a +// temporary object does not live long enough. +// +// When applied to a reference parameter, the referenced object is assumed to be +// retained by the return value of the function. When applied to a non-reference +// parameter (for example, a pointer or a class type), all temporaries +// referenced by the parameter are assumed to be retained by the return value of +// the function. +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +// https://learn.microsoft.com/en-us/cpp/code-quality/c26816?view=msvc-170 +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(msvc::lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[msvc::lifetimebound]] +#elif ABSL_HAVE_ATTRIBUTE(lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound)) +#else +#define ABSL_ATTRIBUTE_LIFETIME_BOUND +#endif + +// Internal attribute; name and documentation TBD. +// +// See the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetime_capture_by +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetime_capture_by) +#define ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(Owner) \ + [[clang::lifetime_capture_by(Owner)]] +#else +#define ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(Owner) +#endif + +// Internal attribute; name and documentation TBD. +// +// See the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetime_capture_by_this +// +// Note: ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this) is deprecated. Use +// ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY_THIS instead. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetime_capture_by_this) +#define ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY_THIS \ + [[clang::lifetime_capture_by_this]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetime_capture_by) +#define ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY_THIS \ + ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this) +#else +#define ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY_THIS +#endif + +// ABSL_ATTRIBUTE_VIEW indicates that a type is solely a "view" of data that it +// points to, similarly to a span, string_view, or other non-owning reference +// type. +// This enables diagnosing certain lifetime issues similar to those enabled by +// ABSL_ATTRIBUTE_LIFETIME_BOUND, such as: +// +// struct ABSL_ATTRIBUTE_VIEW StringView { +// template +// StringView(const R&); +// }; +// +// StringView f(std::string s) { +// return s; // warning: address of stack memory returned +// } +// +// We disable this on Clang versions < 13 because of the following +// false-positive: +// +// absl::string_view f(std::optional sv) { return *sv; } +// +// See the following links for details: +// https://reviews.llvm.org/D64448 +// https://lists.llvm.org/pipermail/cfe-dev/2018-November/060355.html +#if ABSL_HAVE_CPP_ATTRIBUTE(gsl::Pointer) && \ + (!defined(__clang_major__) || __clang_major__ >= 13) +#define ABSL_ATTRIBUTE_VIEW [[gsl::Pointer]] +#else +#define ABSL_ATTRIBUTE_VIEW +#endif + +// ABSL_ATTRIBUTE_OWNER indicates that a type is a container, smart pointer, or +// similar class that owns all the data that it points to. +// This enables diagnosing certain lifetime issues similar to those enabled by +// ABSL_ATTRIBUTE_LIFETIME_BOUND, such as: +// +// struct ABSL_ATTRIBUTE_VIEW StringView { +// template +// StringView(const R&); +// }; +// +// struct ABSL_ATTRIBUTE_OWNER String {}; +// +// StringView f(String s) { +// return s; // warning: address of stack memory returned +// } +// +// We disable this on Clang versions < 13 because of the following +// false-positive: +// +// absl::string_view f(std::optional sv) { return *sv; } +// +// See the following links for details: +// https://reviews.llvm.org/D64448 +// https://lists.llvm.org/pipermail/cfe-dev/2018-November/060355.html +#if ABSL_HAVE_CPP_ATTRIBUTE(gsl::Owner) && \ + (!defined(__clang_major__) || __clang_major__ >= 13) +#define ABSL_ATTRIBUTE_OWNER [[gsl::Owner]] +#else +#define ABSL_ATTRIBUTE_OWNER +#endif + +// ABSL_ATTRIBUTE_TRIVIAL_ABI +// Indicates that a type is "trivially relocatable" -- meaning it can be +// relocated without invoking the constructor/destructor, using a form of move +// elision. +// +// From a memory safety point of view, putting aside destructor ordering, it's +// safe to apply ABSL_ATTRIBUTE_TRIVIAL_ABI if an object's location +// can change over the course of its lifetime: if a constructor can be run one +// place, and then the object magically teleports to another place where some +// methods are run, and then the object teleports to yet another place where it +// is destroyed. This is notably not true for self-referential types, where the +// move-constructor must keep the self-reference up to date. If the type changed +// location without invoking the move constructor, it would have a dangling +// self-reference. +// +// The use of this teleporting machinery means that the number of paired +// move/destroy operations can change, and so it is a bad idea to apply this to +// a type meant to count the number of moves. +// +// Warning: applying this can, rarely, break callers. Objects passed by value +// will be destroyed at the end of the call, instead of the end of the +// full-expression containing the call. In addition, it changes the ABI +// of functions accepting this type by value (e.g. to pass in registers). +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi +// +// b/321691395 - This is currently disabled in open-source builds since +// compiler support differs. If system libraries compiled with GCC are mixed +// with libraries compiled with Clang, types will have different ideas about +// their ABI, leading to hard to debug crashes. +#define ABSL_ATTRIBUTE_TRIVIAL_ABI + +// ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS +// +// Indicates a data member can be optimized to occupy no space (if it is empty) +// and/or its tail padding can be used for other members. +// +// For code that is assured to only build with C++20 or later, prefer using +// the standard attribute `[[no_unique_address]]` directly instead of this +// macro. +// +// https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#c20-no_unique_address +// Current versions of MSVC have disabled `[[no_unique_address]]` since it +// breaks ABI compatibility, but offers `[[msvc::no_unique_address]]` for +// situations when it can be assured that it is desired. Since Abseil does not +// claim ABI compatibility in mixed builds, we can offer it unconditionally. +#if defined(_MSC_VER) && _MSC_VER >= 1929 +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(no_unique_address) +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS +#endif + +// ABSL_ATTRIBUTE_UNINITIALIZED +// +// GCC and Clang support a flag `-ftrivial-auto-var-init=