Skip to content

Commit 4e3bedb

Browse files
committed
Don't copy __FILE__ string when unnecessary
This reduces the amount of dynamic allocations done before main() runs. And as a result also the amount of false positives from rudimentary memory leak detectors (malloc/free counters that use atexit() as the moment where all memory should be freed again).
1 parent 9fe13ae commit 4e3bedb

File tree

7 files changed

+39
-25
lines changed

7 files changed

+39
-25
lines changed

include/cucumber-cpp/internal/CukeEngine.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ class CUCUMBER_CPP_EXPORT StepMatchArg {
2020

2121
class CUCUMBER_CPP_EXPORT StepMatch {
2222
public:
23+
StepMatch()
24+
: source() // Explicitly calling the default constructor, will zero-init for POD types
25+
{}
26+
2327
std::string id;
2428
std::vector<StepMatchArg> args;
25-
std::string source;
29+
const char* source;
2630
std::string regexp;
2731
};
2832

include/cucumber-cpp/internal/step/StepMacros.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,14 @@ CUKE_OBJECT_( \
2525
) \
2626
/**/
2727

28+
#define CUKE_STRINGIFY_BASE_(x) #x
29+
#define CUKE_STRINGIFY_(x) CUKE_STRINGIFY_BASE_(x)
30+
#define CUKE_AT_ __FILE__ ":" CUKE_STRINGIFY_(__LINE__)
31+
/**/
32+
2833
#define CUKE_STEP_REGISTRATION_(step_name, step_matcher) \
2934
::cucumber::internal::registerStep< step_name >( \
30-
step_matcher, __FILE__, __LINE__ \
35+
step_matcher, CUKE_AT_ \
3136
) \
3237
/**/
3338

include/cucumber-cpp/internal/step/StepManager.hpp

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef CUKE_STEPMANAGER_HPP_
22
#define CUKE_STEPMANAGER_HPP_
33

4+
#include <cstring>
45
#include <map>
56
#include <sstream>
67
#include <stdexcept>
@@ -101,13 +102,13 @@ class CUCUMBER_CPP_EXPORT InvokeResult {
101102

102103
class CUCUMBER_CPP_EXPORT StepInfo : public boost::enable_shared_from_this<StepInfo> {
103104
public:
104-
StepInfo(const std::string &stepMatcher, const std::string source);
105+
StepInfo(const std::string &stepMatcher, const char* const source);
105106
SingleStepMatch matches(const std::string &stepDescription) const;
106107
virtual InvokeResult invokeStep(const InvokeArgs * pArgs) const = 0;
107108

108109
step_id_type id;
109110
Regex regex;
110-
const std::string source;
111+
const char* const source;
111112
private:
112113
// Shut up MSVC warning C4512: assignment operator could not be generated
113114
StepInfo& operator=(const StepInfo& other);
@@ -166,7 +167,7 @@ class CUCUMBER_CPP_EXPORT BasicStep {
166167
template<class T>
167168
class StepInvoker : public StepInfo {
168169
public:
169-
StepInvoker(const std::string &stepMatcher, const std::string source);
170+
StepInvoker(const std::string &stepMatcher, const char* const source);
170171

171172
InvokeResult invokeStep(const InvokeArgs *args) const;
172173
};
@@ -192,23 +193,27 @@ class CUCUMBER_CPP_EXPORT StepManager {
192193
};
193194

194195

195-
static inline std::string toSourceString(const char *filePath, const int line) {
196-
using namespace std;
197-
stringstream s;
198-
string file(filePath);
199-
string::size_type pos = file.find_last_of("/\\");
200-
if (pos == string::npos) {
201-
s << file;
196+
static inline const char* toSourceString(const char* const fileLocation) {
197+
using std::strrchr;
198+
const char* pos = strrchr(fileLocation, '/');
199+
if (pos == NULL) {
200+
pos = strrchr(fileLocation, '\\');
202201
} else {
203-
s << file.substr(++pos);
202+
const char* maybeNewPos = strrchr(pos, '\\');
203+
if (maybeNewPos != NULL) {
204+
pos = maybeNewPos;
205+
}
206+
}
207+
if (pos == NULL) {
208+
return fileLocation;
209+
} else {
210+
return pos + 1;
204211
}
205-
s << ":" << line;
206-
return s.str();
207212
}
208213

209214
template<class T>
210-
static int registerStep(const std::string &stepMatcher, const char *file, const int line) {
211-
return StepManager::addStep(boost::make_shared< StepInvoker<T> >(stepMatcher, toSourceString(file, line)));
215+
static int registerStep(const std::string &stepMatcher, const char* const fileLocation) {
216+
return StepManager::addStep(boost::make_shared< StepInvoker<T> >(stepMatcher, toSourceString(fileLocation)));
212217
}
213218

214219
template<typename T>
@@ -251,7 +256,7 @@ const T BasicStep::getInvokeArg() {
251256

252257

253258
template<class T>
254-
StepInvoker<T>::StepInvoker(const std::string &stepMatcher, const std::string source) :
259+
StepInvoker<T>::StepInvoker(const std::string &stepMatcher, const char* const source) :
255260
StepInfo(stepMatcher, source) {
256261
}
257262

src/StepManager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace cucumber {
44
namespace internal {
55

66

7-
StepInfo::StepInfo(const std::string &stepMatcher, const std::string source) :
7+
StepInfo::StepInfo(const std::string &stepMatcher, const char* const source) :
88
regex(stepMatcher),
99
source(source) {
1010
static step_id_type currentId = 0;

src/connectors/wire/WireProtocol.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,8 @@ namespace {
272272
jsonArgs.push_back(jsonMa);
273273
}
274274
jsonM["args"] = jsonArgs;
275-
if (!m.source.empty()) {
276-
jsonM["source"] = m.source;;
275+
if (m.source && strlen(m.source) > 0) {
276+
jsonM["source"] = m.source;
277277
}
278278
if (!m.regexp.empty()) {
279279
jsonM["regexp"] = m.regexp;

tests/integration/StepRegistrationTest.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ class ManualStep : public GenericStep {
1414
//private:
1515
static const int cukeRegId;
1616
};
17-
const int ManualStep::cukeRegId = ::cucumber::internal::registerStep<ManualStep>(MANUAL_STEP_MATCHER, "C:\\Path\\With/Barward/And\\Forward/Slashes.cpp", 42);
17+
const int ManualStep::cukeRegId = ::cucumber::internal::registerStep<ManualStep>(MANUAL_STEP_MATCHER, "C:\\Path\\With/Barward/And\\Forward/Slashes.cpp:42");
1818

1919
TEST(StepRegistrationTest, manualRegistration) {
2020
// static_cast is necessary for GTest <= 1.7 and C++ >= 2011 because it
2121
// doesn't support contextually-convertible-to-bool
2222
EXPECT_TRUE(static_cast<bool>(StepManager::stepMatches(MANUAL_STEP_MATCHER)));
23-
EXPECT_EQ("Slashes.cpp:42", StepManager::getStep(ManualStep::cukeRegId)->source);
23+
EXPECT_STREQ("Slashes.cpp:42", StepManager::getStep(ManualStep::cukeRegId)->source);
2424
}
2525

2626
#define GIVEN_MATCHER "given matcher"

tests/utils/StepManagerTestDouble.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace internal {
99

1010
class StepInfoNoOp : public StepInfo {
1111
public:
12-
StepInfoNoOp(const std::string &stepMatcher, const std::string source) : StepInfo(stepMatcher, source) {}
12+
StepInfoNoOp(const std::string &stepMatcher, const char* const source) : StepInfo(stepMatcher, source) {}
1313
InvokeResult invokeStep(const InvokeArgs*) const {
1414
return InvokeResult::success();
1515
}
@@ -69,7 +69,7 @@ class StepManagerTestDouble : public StepManager {
6969
}
7070

7171
static step_id_type addStepDefinitionWithId(step_id_type desiredId, const std::string &stepMatcher,
72-
const std::string source) {
72+
const char* const source) {
7373
boost::shared_ptr<StepInfo> stepInfo(boost::make_shared<StepInfoNoOp>(stepMatcher, source));
7474
stepInfo->id = desiredId;
7575
return addStep(stepInfo);

0 commit comments

Comments
 (0)