I am trying to add instrumentation to a widely used template class in my product. I am currently on VS 2019 (16.10.4)
with /std:c++17
. The new feature of std::source_location
is a great addition for the kind of task I am interested in solving. While std::source_location
and std::experimental::source_location
are not available on my compiler, I built my own based on this answer. My goal is make changes to the class constructor/members in a special build and run tests. The changes to the class itself does not change its usage, so rest of the code remains same.
This all compiles and works great - mostly. Except, I run into the copy elision which almost beats the purpose of using std::source_location
. I'd like my source_location
to be the location at which the caller variable is defined instead of where the actual object is created.
This problem can be demonstrated on gcc -std=c++20
with and without -fno-elide-constructors
as well. See simplified version of my minimal reproducible godbolt sample.
MyClass:
class MyClass
{
private:
int m_a = 0;
std::source_location m_location;
public:
MyClass(std::source_location location = std::source_location::current())
: m_location(location)
{
}
MyClass(const MyClass &other, std::source_location location = std::source_location::current())
: m_a(other.m_a)
, m_location(location)
{
}
MyClass(MyClass &&other, std::source_location location = std::source_location::current())
: m_a(other.m_a)
, m_location(location)
{
}
};
Usage:
MyClass getCopy1()
{
MyClass ret;
return ret;
}
MyClass getCopy2()
{
return MyClass();
}
int main()
{
MyClass o1;
MyClass o2(o1);
MyClass o3(getCopy1());
MyClass o4(getCopy2());
std::cout << "o1: " << o1.getLocationInfo() << std::endl;
std::cout << "o2: " << o2.getLocationInfo() << std::endl;
std::cout << "o3: " << o3.getLocationInfo() << std::endl;
std::cout << "o4: " << o4.getLocationInfo() << std::endl;
return 0;
}
Actual Output:
o1: /app/example.cpp(56:13) int main()
o2: /app/example.cpp(57:18) int main()
o3: /app/example.cpp(46:12) MyClass getCopy1()
o4: /app/example.cpp(51:20) MyClass getCopy2()
Expected Output:
o1: /app/example.cpp(56:13) int main()
o2: /app/example.cpp(57:18) int main()
o3: /app/example.cpp(58:26) int main()
o4: /app/example.cpp(59:26) int main()
Workaround 1 (explicit argument):
MyClass o3(getCopy1(), std::source_location::current());
MyClass o4(getCopy2(), std::source_location::current());
Workaround 2 (std::move
):
MyClass o3(std::move(getCopy1()));
MyClass o4(std::move(getCopy2()));
While any of these workarounds above give me the results I desire, they are impractical. These will require me to change several instances of usage code and will beat the purpose of using something like std::source_location
. Any changes to the MyClass.h/cpp
that does not break its usage or any changes to the compiler flags is fair game. I intend to have a separate instrumented build that will not be used in production. My product currently builds on VS 2019 (16.10.4)
with /std:c++17
.