Reputation: 45
I have a custom class MyNameSpace::String
for which I created a summary in lldb. For instance, for a MyNameSpace::String myString="a string"
, here is the summary in lldb:
frame variable myString
(MyNameSpace::String) myString = "a string"
How can I get a summary for a std::vector<MyNameSpace::String>vecString
that look as a summary of a std::vector<std::string>vec
?
So far, here are results of the command frame variable
on the different variables:
vector of standard string
frame variable vec
(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >) vec = size=3 {
[0] = "bonjour"
[1] = "je suis"
[2] = "vincent"
}
vector of my custom String
frame variable vecString
(std::__1::vector< MyNameSpace::String, std::__1::allocator< MyNameSpace::String> >) vecString = size=2 {
[0] = {
value = 7453010373645639777
}
[1] = {
value = 18302628889929726940
}
}
How to get my custom summary to be applied to my vector's elements?
Thank you very much for any help.
V.
Edit: in response to comment
I am using lldb-310.2.37.
I created the summary for my String class with a script:
def generic_summary(valueObject,dict):
name=valueObject.GetName()
return valueObject.GetFrame().EvaluateExpression(name+".toString().toStdString()").GetSummary()
debugger.HandleCommand('type summary add MyNameSpace::String -F CustomSummaries.generic_summary')
that goes with the proper command in ~/.lldbinit.
frame variable -T vecString
:
(lldb) frame variable -T vecString
(std::__1::vector<MyNameSpace::String, std::__1::allocator< MyNameSpace::String> >) vecString = size=2 {
(MyNameSpace::String) [0] = {
(MyNameSpace::uint8) value = 7453010373645639777
}
(MyNameSpace::String) [1] = {
(MyNameSpace::uint8) value = 18302628889929726844
}
}
Upvotes: 3
Views: 1494
Reputation: 3329
So, as you realized - correctly - your problem is here:
name=valueObject.GetName()
The true answer to your question is "don't do what you're doing". Let's take this step by step.
You are here asking a value for its name. If I have
struct Foo { int x; int y; } aFoo;
the name of the SBValue for "x" will be "x". If you want "aFoo.x", what you want to ask for is the expression path, which - as its name implies - is what you would type in an expression to refer to that value.
Things get trickier when synthetic children show up though.
You might think your element [0] to have an expression path of vecString[0]. But it won't. It will most likely be just [0] again. Why?
That has to do with the way synthetic children are created. Specifically, while "x" in "aFoo" knows that its parent object is the "aFoo" struct, the [0] element of vecString does not. It is created from whole cloth as a value whose location is a certain memory address. Given the layout of a libc++ vector:
struct vector<T> { T* begin; T* end; T* endStorage; };
the formula is locationOfXElement = begin + X*sizeof(T)
(this is all sketched out and pseudo-code, but cope with me!)
What LLDB does is, compute that location, and then say - well, now create a ValueObject (the internal term for SBValue, if you wish) at that location in memory. This has no connection to the underlying vector whatsoever. It's just a value at some location. Hence, the notion of expression path gets it wrong.
"But you could fix that!", you proclaim.
Yes, we probably could. It would take the notion that a synthetic child has a logical parent, as well as the notion of what kind of access to the structure is being simulated: is it array-like? pointer-like? struct-like? That is so one would know to write myObject[N], vs. myObject->foo vs. myObject.bar
So that could be done. It would still not solve your problem though.
Instead of a vector, now consider
std::map<yourString,int> myMap;
LLDB presents this as a glorified set of
pair<myString,int> { myString key; int value };
named [0],[1],...
Let's say we get the expression path syntax right, and we present this as
myMap[0].key
Try using that in an expression! Yes - you guessed it - it will fail! myMap::operator[] expects a string, not a numeric index. In this case, the very mode of presentation chosen by LLDB breaks the usability of synthetic children in code, no matter how smart expression paths are.
I am sure that could be fixed too, but at the end of the day, there are good reasons to not use synthetic children when evaluating expressions. Consider the following:
vecString[0].size()
Do you want your vector element to be accessed by operator[]? Or do you want the synthetic children to be used?
And what about this?
vecString[0] = "hello"
And now?
vecString[0] = vecString[0] + " world!"
In general, the problem of mixing synthetic children and expressions is tricky enough that we went with "no no" - synthetic children are totally transparent to the expression evaluator.
And now we get back to "don't do what you're doing", don't try running code in a formatter unless you really really really really really have to, and even then be ready for headaches like this.
I will assume that "toString().toStdString()" is essentially doing some memory accesses and some computation - what you want to be doing is replicate that in Python code using memory read operations and doing your computations in Python code instead of perusing the expression evaluator.
The resulting formatter will be faster, potentially much more reliable - and it will work in all cases.
Upvotes: 4