Skip to content

Commit a01e4d5

Browse files
bcaimanoevergreen
authored andcommitted
SERVER-42894 Create SourceLocation type and related macros
1 parent 2a82448 commit a01e4d5

File tree

4 files changed

+385
-0
lines changed

4 files changed

+385
-0
lines changed

src/mongo/platform/SConscript

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ env.CppUnitTest(
1313
'mutex_test.cpp',
1414
'process_id_test.cpp',
1515
'random_test.cpp',
16+
'source_location_test.cpp',
1617
'stack_locator_test.cpp',
1718
'decimal128_test.cpp',
1819
'decimal128_bson_test.cpp',
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/**
2+
* Copyright (C) 2019-present MongoDB, Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the Server Side Public License, version 1,
6+
* as published by MongoDB, Inc.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* Server Side Public License for more details.
12+
*
13+
* You should have received a copy of the Server Side Public License
14+
* along with this program. If not, see
15+
* <http://www.mongodb.com/licensing/server-side-public-license>.
16+
*
17+
* As a special exception, the copyright holders give permission to link the
18+
* code of portions of this program with the OpenSSL library under certain
19+
* conditions as described in each individual source file and distribute
20+
* linked combinations including the program with the OpenSSL library. You
21+
* must comply with the Server Side Public License in all respects for
22+
* all of the code used other than as permitted herein. If you modify file(s)
23+
* with this exception, you may extend this exception to your version of the
24+
* file(s), but you are not obligated to do so. If you do not wish to do so,
25+
* delete this exception statement from your version. If you delete this
26+
* exception statement from all source files in the program, then also delete
27+
* it in the license file.
28+
*/
29+
30+
#pragma once
31+
32+
#include <cstdint>
33+
#include <string>
34+
35+
#if !defined(_MSC_VER) && !defined(__clang__) // not windows or clang
36+
#include <experimental/source_location>
37+
#endif // windows
38+
39+
#include <fmt/format.h>
40+
41+
namespace mongo {
42+
43+
/**
44+
* A SourceLocation is a constexpr type that captures a source location
45+
*
46+
* This class mimics the api signature of C++20 std::source_location
47+
*
48+
* It is intended to be constructed with MONGO_SOURCE_LOCATION() below.
49+
*/
50+
class SourceLocation {
51+
public:
52+
constexpr SourceLocation(uint_least32_t line,
53+
uint_least32_t column,
54+
const char* file_name,
55+
const char* function_name) noexcept
56+
: _line(line), _column(column), _file_name(file_name), _function_name(function_name) {}
57+
58+
constexpr uint_least32_t line() const noexcept {
59+
return _line;
60+
}
61+
62+
constexpr uint_least32_t column() const noexcept {
63+
return _column;
64+
}
65+
66+
constexpr const char* file_name() const noexcept {
67+
return _file_name;
68+
}
69+
70+
constexpr const char* function_name() const noexcept {
71+
return _function_name;
72+
}
73+
74+
private:
75+
uint_least32_t _line;
76+
uint_least32_t _column; // column will be 0 if there isn't compiler support
77+
const char* _file_name;
78+
const char* _function_name;
79+
};
80+
81+
/**
82+
* SourceLocationHolder is intended for convenient io of SourceLocation
83+
*/
84+
class SourceLocationHolder {
85+
public:
86+
constexpr SourceLocationHolder(SourceLocation&& loc) noexcept
87+
: _loc(std::forward<SourceLocation>(loc)) {}
88+
89+
constexpr uint_least32_t line() const noexcept {
90+
return _loc.line();
91+
}
92+
93+
constexpr uint_least32_t column() const noexcept {
94+
return _loc.column();
95+
}
96+
97+
constexpr const char* file_name() const noexcept {
98+
return _loc.file_name();
99+
}
100+
101+
constexpr const char* function_name() const noexcept {
102+
return _loc.function_name();
103+
}
104+
105+
std::string toString() const {
106+
using namespace fmt::literals;
107+
return R"({{fileName:"{}", line:{}, functionName:"{}"}})"_format(
108+
_loc.file_name(), _loc.line(), _loc.function_name());
109+
}
110+
111+
friend std::ostream& operator<<(std::ostream& out, const SourceLocationHolder& context) {
112+
return out << context.toString();
113+
}
114+
115+
private:
116+
SourceLocation _loc;
117+
};
118+
119+
/**
120+
* MONGO_SOURCE_LOCATION() either:
121+
* - captures std::experimental::source_location::current()
122+
* - makes a best effort with various macros and local static constants
123+
*
124+
* Since __FUNCSIG__ and __PRETTY_FUNCTION__ aren't defined outside of functions, there is also
125+
* MONGO_SOURCE_LOCATION_NO_FUNC() for use with a default member initializatizer or constant
126+
* initialization.
127+
*/
128+
#if defined(_MSC_VER) // windows
129+
130+
// MSVC does not have any of N4810 yet. (see
131+
// https://developercommunity.visualstudio.com/idea/354069/implement-c-library-fundamentals-ts-v2.html)
132+
#define MONGO_SOURCE_LOCATION() SourceLocation(__LINE__, 0ul, __FILE__, __func__)
133+
#define MONGO_SOURCE_LOCATION_NO_FUNC() SourceLocation(__LINE__, 0ul, __FILE__, "")
134+
135+
#elif defined(__clang__) // windows -> clang
136+
137+
// Clang got __builtin_FILE et al as of 8.0.1 (see https://reviews.llvm.org/D37035)
138+
#define MONGO_SOURCE_LOCATION() SourceLocation(__LINE__, 0ul, __FILE__, __func__)
139+
#define MONGO_SOURCE_LOCATION_NO_FUNC() SourceLocation(__LINE__, 0ul, __FILE__, "")
140+
141+
#elif defined(__GNUG__) // clang -> gcc
142+
143+
constexpr auto toSourceLocation(std::experimental::source_location loc) {
144+
// Note that std::experimental::source_location captures __func__, not __PRETTY_FUNC__
145+
return SourceLocation(loc.line(), loc.column(), loc.file_name(), loc.function_name());
146+
}
147+
148+
#define MONGO_SOURCE_LOCATION() toSourceLocation(std::experimental::source_location::current())
149+
#define MONGO_SOURCE_LOCATION_NO_FUNC() \
150+
toSourceLocation(std::experimental::source_location::current())
151+
152+
#else // gcc -> ?
153+
154+
#error "Unknown compiler, cannot approximate std::source_location"
155+
156+
#endif // ?
157+
158+
} // namespace mongo
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/**
2+
* Copyright (C) 2019-present MongoDB, Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the Server Side Public License, version 1,
6+
* as published by MongoDB, Inc.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* Server Side Public License for more details.
12+
*
13+
* You should have received a copy of the Server Side Public License
14+
* along with this program. If not, see
15+
* <http://www.mongodb.com/licensing/server-side-public-license>.
16+
*
17+
* As a special exception, the copyright holders give permission to link the
18+
* code of portions of this program with the OpenSSL library under certain
19+
* conditions as described in each individual source file and distribute
20+
* linked combinations including the program with the OpenSSL library. You
21+
* must comply with the Server Side Public License in all respects for
22+
* all of the code used other than as permitted herein. If you modify file(s)
23+
* with this exception, you may extend this exception to your version of the
24+
* file(s), but you are not obligated to do so. If you do not wish to do so,
25+
* delete this exception statement from your version. If you delete this
26+
* exception statement from all source files in the program, then also delete
27+
* it in the license file.
28+
*/
29+
30+
#include "mongo/platform/basic.h"
31+
32+
#include "mongo/platform/source_location_test.h"
33+
34+
namespace mongo {
35+
namespace {
36+
37+
constexpr SourceLocation makeLocalFunctionSourceLocationForTest() {
38+
return MONGO_SOURCE_LOCATION();
39+
}
40+
41+
const SourceLocationHolder kLocation = MONGO_SOURCE_LOCATION_NO_FUNC();
42+
43+
struct StructWithDefaultInitContextMember {
44+
const SourceLocationHolder location = MONGO_SOURCE_LOCATION_NO_FUNC();
45+
};
46+
47+
#define CALL_MONGO_SOURCE_LOCATION() MONGO_SOURCE_LOCATION()
48+
49+
TEST(SourceLocation, CorrectLineNumber) {
50+
using LineT = std::invoke_result_t<decltype(&SourceLocation::line), SourceLocation>;
51+
ASSERT_EQ(MONGO_SOURCE_LOCATION().line(), static_cast<LineT>(__LINE__));
52+
}
53+
54+
TEST(SourceLocation, InlineVariable) {
55+
SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
56+
SourceLocationHolder inlineLocation2 = MONGO_SOURCE_LOCATION();
57+
SourceLocationHolder inlineLocation3 = MONGO_SOURCE_LOCATION();
58+
59+
// Each location should have the same filename
60+
ASSERT_EQ(inlineLocation1.file_name(), inlineLocation2.file_name());
61+
ASSERT_EQ(inlineLocation1.file_name(), inlineLocation3.file_name());
62+
63+
// The line numbers for each location should increase monotonically when inline
64+
ASSERT_LT(inlineLocation1.line(), inlineLocation2.line());
65+
ASSERT_LT(inlineLocation2.line(), inlineLocation3.line());
66+
67+
unittest::log() << inlineLocation1;
68+
unittest::log() << inlineLocation2;
69+
unittest::log() << inlineLocation3;
70+
}
71+
72+
TEST(SourceLocation, LocalFunction) {
73+
SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
74+
SourceLocationHolder localFunctionLocation1 = makeLocalFunctionSourceLocationForTest();
75+
SourceLocationHolder localFunctionLocation2 = makeLocalFunctionSourceLocationForTest();
76+
77+
// The inline location should have the same file name but a later line
78+
ASSERT_EQ(inlineLocation1.file_name(), localFunctionLocation1.file_name());
79+
ASSERT_GT(inlineLocation1.line(), localFunctionLocation1.line());
80+
81+
// The two local function locations should be identical
82+
ASSERT_EQ(localFunctionLocation1, localFunctionLocation2);
83+
84+
unittest::log() << inlineLocation1;
85+
unittest::log() << localFunctionLocation1;
86+
unittest::log() << localFunctionLocation2;
87+
}
88+
89+
TEST(SourceLocation, HeaderFunction) {
90+
SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
91+
SourceLocationHolder headerLocation1 = makeHeaderSourceLocationForTest();
92+
SourceLocationHolder headerLocation2 = makeHeaderSourceLocationForTest();
93+
94+
// The inline location should have a different file name
95+
ASSERT_NE(inlineLocation1.file_name(), headerLocation1.file_name());
96+
97+
// The two header locations should be identical
98+
ASSERT_EQ(headerLocation1, headerLocation2);
99+
100+
unittest::log() << inlineLocation1;
101+
unittest::log() << headerLocation1;
102+
unittest::log() << headerLocation2;
103+
}
104+
105+
TEST(SourceLocation, GlobalVariable) {
106+
SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
107+
108+
// The inline location should have the same file name but a later line
109+
ASSERT_EQ(inlineLocation1.file_name(), kLocation.file_name());
110+
ASSERT_GT(inlineLocation1.line(), kLocation.line());
111+
112+
unittest::log() << inlineLocation1;
113+
unittest::log() << kLocation;
114+
}
115+
116+
TEST(SourceLocation, DefaultStructMember) {
117+
SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
118+
StructWithDefaultInitContextMember obj1;
119+
StructWithDefaultInitContextMember obj2;
120+
121+
// The inline location should have the same file name but a later line
122+
ASSERT_EQ(inlineLocation1.file_name(), obj1.location.file_name());
123+
ASSERT_GT(inlineLocation1.line(), obj1.location.line());
124+
125+
// The two default ctor'd struct member locations should be identical
126+
ASSERT_EQ(obj1.location, obj2.location);
127+
128+
unittest::log() << inlineLocation1;
129+
unittest::log() << obj1.location;
130+
unittest::log() << obj2.location;
131+
}
132+
133+
TEST(SourceLocation, Macro) {
134+
SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
135+
SourceLocationHolder inlineLocation2 = CALL_MONGO_SOURCE_LOCATION();
136+
137+
// Each location should have the same filename
138+
ASSERT_EQ(inlineLocation1.file_name(), inlineLocation2.file_name());
139+
140+
// The line numbers for each location should increase monotonically when inline
141+
ASSERT_LT(inlineLocation1.line(), inlineLocation2.line());
142+
143+
unittest::log() << inlineLocation1;
144+
unittest::log() << inlineLocation2;
145+
}
146+
147+
TEST(SourceLocation, Constexpr) {
148+
constexpr SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
149+
constexpr SourceLocationHolder inlineLocation2 = MONGO_SOURCE_LOCATION();
150+
static_assert((inlineLocation1.line() + 1) == inlineLocation2.line());
151+
static_assert(inlineLocation1.column() == inlineLocation2.column());
152+
static_assert(areEqual(inlineLocation1.file_name(), inlineLocation2.file_name()));
153+
static_assert(areEqual(inlineLocation1.function_name(), inlineLocation2.function_name()));
154+
155+
constexpr auto localFunctionLocation = makeLocalFunctionSourceLocationForTest();
156+
static_assert(inlineLocation1.line() > localFunctionLocation.line());
157+
static_assert(areEqual(inlineLocation1.file_name(), localFunctionLocation.file_name()));
158+
static_assert(
159+
!areEqual(inlineLocation1.function_name(), localFunctionLocation.function_name()));
160+
161+
constexpr auto headerLocation = makeHeaderSourceLocationForTest();
162+
static_assert(!areEqual(inlineLocation1.file_name(), headerLocation.file_name()));
163+
static_assert(!areEqual(inlineLocation1.function_name(), headerLocation.function_name()));
164+
}
165+
166+
} // namespace
167+
} // namespace mongo
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Copyright (C) 2019-present MongoDB, Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the Server Side Public License, version 1,
6+
* as published by MongoDB, Inc.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* Server Side Public License for more details.
12+
*
13+
* You should have received a copy of the Server Side Public License
14+
* along with this program. If not, see
15+
* <http://www.mongodb.com/licensing/server-side-public-license>.
16+
*
17+
* As a special exception, the copyright holders give permission to link the
18+
* code of portions of this program with the OpenSSL library under certain
19+
* conditions as described in each individual source file and distribute
20+
* linked combinations including the program with the OpenSSL library. You
21+
* must comply with the Server Side Public License in all respects for
22+
* all of the code used other than as permitted herein. If you modify file(s)
23+
* with this exception, you may extend this exception to your version of the
24+
* file(s), but you are not obligated to do so. If you do not wish to do so,
25+
* delete this exception statement from your version. If you delete this
26+
* exception statement from all source files in the program, then also delete
27+
* it in the license file.
28+
*/
29+
30+
#pragma once
31+
32+
#include "mongo/unittest/unittest.h"
33+
34+
#include "mongo/platform/source_location.h"
35+
36+
namespace mongo {
37+
inline bool operator==(const SourceLocationHolder& lhs, const SourceLocationHolder& rhs) {
38+
return lhs.line() == rhs.line() //
39+
&& lhs.column() == rhs.column() //
40+
&& lhs.file_name() == rhs.file_name() //
41+
&& lhs.function_name() == rhs.function_name();
42+
}
43+
44+
inline bool operator!=(const SourceLocationHolder& lhs, const SourceLocationHolder& rhs) {
45+
return !(lhs == rhs);
46+
}
47+
48+
// Simple recursive constexpr string comparison to play nice with static_assert
49+
constexpr bool areEqual(const char* string1, const char* string2) {
50+
return (string1 != nullptr) //
51+
&& (string2 != nullptr) //
52+
&& *string1 == *string2 //
53+
&& (*string1 == '\0' || areEqual(string1 + 1, string2 + 1));
54+
}
55+
56+
inline constexpr SourceLocation makeHeaderSourceLocationForTest() {
57+
return MONGO_SOURCE_LOCATION();
58+
}
59+
} // namespace mongo

0 commit comments

Comments
 (0)