Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 60 additions & 53 deletions strings/base_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,53 @@ struct IUnknown;
typedef struct _GUID GUID;
#endif

// std::source_location is a C++20 feature, which is above the C++17 feature floor for cppwinrt. The source location needs
// to be the calling code, not cppwinrt itself, so that it is useful to developers building on top of this library. As a
// result any public-facing method that can result in an error needs a default-constructed source_location argument. Because
// this type does not exist in C++17 we need to use a macro to optionally add parameters and forwarding wherever appropriate.
#if defined(__cpp_consteval)
#define WINRT_IMPL_CONSTEVAL consteval
#else
#define WINRT_IMPL_CONSTEVAL constexpr
#endif

// The intrinsics (such as __builtin_FILE()) that power std::source_location are also used to power winrt:impl::slim_source_location.
// The source location needs to be for the calling code, not cppwinrt itself, so that it is useful to developers building on top of
// this library. As a result any public-facing method that can result in an error needs a default-constructed slim_source_location
// argument so that it will collect source information from the application code that is calling into cppwinrt.
//
// Some projects may decide to disable std::source_location support to prevent source code information from ending up in their
// release binaries, or to reduce binary size. Defining WINRT_NO_SOURCE_LOCATION will prevent this feature from activating.
#if defined(__cpp_lib_source_location) && !defined(WINRT_NO_SOURCE_LOCATION)
// We do not directly use std::source_location for two reasons:
// 1) std::source_location::function_name() is unavoidable. These strings end up in the final binary, bloating their size. This
// is particularly impactful for code bases that use templates heavily. Cases of 50% binary size growth have been observed.
// 2) std::source_location is a cpp20 feature, which is above the cpp17 feature floor for cppwinrt. By defining our own version
// we can avoid ODR violations in mixed cpp17/cpp20 builds. cpp17 callers will have an ABI that matches cpp20 callers (they
// will just not have useful file/line/function information).
//
// Some projects may decide that the source information binary size impact is not worth the benefit. Defining WINRT_NO_SOURCE_LOCATION
// will prevent this feature from activating. The slim_source_location type will be forwarded around but it will not include any
// nonzero data. That eliminates the biggest source of binary size overhead.
//
// To help with debugging the __builtin_FUNCTION() intrinsic will be used in _DEBUG builds. This will provide a bit more diagnostic
// value at the cost of binary size. The assumption is that binary size is considered less important in debug builds so this tradeoff
// is acceptable.

#if !defined(__cpp_lib_source_location)
// cpp17 mode. The source_location intrinsics are not available.
#define WINRT_IMPL_BUILTIN_LINE 0
#define WINRT_IMPL_BUILTIN_FILE nullptr
#define WINRT_IMPL_BUILTIN_FUNCTION nullptr
#elif defined(WINRT_NO_SOURCE_LOCATION)
// The caller has disabled source_location support. Ensure that there is no binary size overhead for line/file/function.
#define WINRT_IMPL_BUILTIN_LINE 0
#define WINRT_IMPL_BUILTIN_FILE nullptr
#define WINRT_IMPL_BUILTIN_FUNCTION nullptr
Comment thread
dmachaj marked this conversation as resolved.
Outdated
#elif _DEBUG
// Debug builds include FUNCTION information, which has a heavy binary size impact.
#define WINRT_IMPL_BUILTIN_LINE __builtin_LINE()
#define WINRT_IMPL_BUILTIN_FILE __builtin_FILE()
#define WINRT_IMPL_BUILTIN_FUNCTION __builtin_FUNCTION()
#else
// Release builds in cpp20 mode get file and line information.
#define WINRT_IMPL_BUILTIN_LINE __builtin_LINE()
#define WINRT_IMPL_BUILTIN_FILE __builtin_FILE()
#define WINRT_IMPL_BUILTIN_FUNCTION nullptr
#endif
Comment thread
dmachaj marked this conversation as resolved.

namespace winrt::impl
{
Expand All @@ -93,19 +132,23 @@ namespace winrt::impl
// have heavy binary size overhead when templates cause many permutations to exist.
struct slim_source_location
{
[[nodiscard]] static consteval slim_source_location current(
const std::uint_least32_t line = __builtin_LINE(),
const char* const file = __builtin_FILE()) noexcept
[[nodiscard]] static WINRT_IMPL_CONSTEVAL slim_source_location current(
Comment thread
dmachaj marked this conversation as resolved.
const std::uint_least32_t line = WINRT_IMPL_BUILTIN_LINE,
const char* const file = WINRT_IMPL_BUILTIN_FILE,
const char* const function = WINRT_IMPL_BUILTIN_FUNCTION) noexcept
{
return slim_source_location{ line, file };
return slim_source_location{ line, file, function };
}

[[nodiscard]] constexpr slim_source_location() noexcept = default;

[[nodiscard]] constexpr slim_source_location(const std::uint_least32_t line,
const char* const file) noexcept :
[[nodiscard]] constexpr slim_source_location(
const std::uint_least32_t line,
const char* const file,
const char* const function) noexcept :
m_line(line),
m_file(file)
m_file(file),
m_function(function)
{}

[[nodiscard]] constexpr std::uint_least32_t line() const noexcept
Expand All @@ -118,39 +161,18 @@ namespace winrt::impl
return m_file;
}

constexpr const char* function_name() const noexcept
[[nodiscard]] constexpr const char* function_name() const noexcept
{
// This is intentionally not included. See comment above.
return nullptr;
return m_function;
}

private:
const std::uint_least32_t m_line{};
const char* const m_file{};
const char* const m_function{};
};
}

// std::source_location includes function_name which can be helpful but creates a lot of binary size impact. Many consumers
// have defined WINRT_NO_SOURCE_LOCATION to prevent this impact, losing the value of source_location. We have defined a
// slim_source_location struct that is equivalent but excludes function_name. This should have the vast majority of the
// usefulness of source_location while having a much smaller binary impact.
//
// When building _DEBUG binary size is not usually much of a concern, so we can use the full source_location type.
#ifdef _DEBUG
#define WINRT_IMPL_SOURCE_LOCATION_ARGS_NO_DEFAULT , std::source_location const& sourceInformation
#define WINRT_IMPL_SOURCE_LOCATION_ARGS , std::source_location const& sourceInformation = std::source_location::current()
#define WINRT_IMPL_SOURCE_LOCATION_ARGS_SINGLE_PARAM std::source_location const& sourceInformation = std::source_location::current()

#define WINRT_IMPL_SOURCE_LOCATION_FORWARD , sourceInformation
#define WINRT_IMPL_SOURCE_LOCATION_FORWARD_SINGLE_PARAM sourceInformation

#define WINRT_SOURCE_LOCATION_ACTIVE

#ifdef _MSC_VER
#pragma detect_mismatch("WINRT_SOURCE_LOCATION", "true")
#endif // _MSC_VER

#else // !_DEBUG
#define WINRT_IMPL_SOURCE_LOCATION_ARGS_NO_DEFAULT , winrt::impl::slim_source_location const& sourceInformation
#define WINRT_IMPL_SOURCE_LOCATION_ARGS , winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()
#define WINRT_IMPL_SOURCE_LOCATION_ARGS_SINGLE_PARAM winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()
Comment thread
dmachaj marked this conversation as resolved.
Outdated
Expand All @@ -163,18 +185,3 @@ namespace winrt::impl
#ifdef _MSC_VER
#pragma detect_mismatch("WINRT_SOURCE_LOCATION", "slim")
#endif // _MSC_VER

#endif // _DEBUG

#else
#define WINRT_IMPL_SOURCE_LOCATION_ARGS_NO_DEFAULT
#define WINRT_IMPL_SOURCE_LOCATION_ARGS
#define WINRT_IMPL_SOURCE_LOCATION_ARGS_SINGLE_PARAM

#define WINRT_IMPL_SOURCE_LOCATION_FORWARD
#define WINRT_IMPL_SOURCE_LOCATION_FORWARD_SINGLE_PARAM

#ifdef _MSC_VER
#pragma detect_mismatch("WINRT_SOURCE_LOCATION", "false")
#endif // _MSC_VER
#endif // defined(__cpp_lib_source_location) && !defined(WINRT_NO_SOURCE_LOCATION)