Swift 4: Pass multidimensional array to c++ - c++

I have next function in c++:
double* solveSystemOfEquatationWithAugmentedMatrix(double **matrix, size_t rows)
On the swift part I have simple [[Double]] that have to passed
I`ve tried next conversation:
let array = UnsafeMutablePointer<UnsafeMutablePointer<Double>>.allocate(capacity: matrix.count)
The I`ve tried to run over each inner pointer and allocate capacity for each inner pointer, but no luck... there is even no allocate function in each inner row.
What is elegant and fast way to do such conversation and pass to c++?

I believe it would be best to create an ObjectiveC wrapper around your matrix buffer. This way you will have access directly to its memory and still be able to use it in swift.
You will need to use your header to control what access you need to the class but here is an example:
#interface Matrix : NSObject
#property (nonatomic, readonly) int rowCount;
#property (nonatomic, readonly) int columnCount;
- (double)elementAt:(int)row column:(int)column;
- (void)setElementAt:(int)row column:(int)column to:(double)value;
- (instancetype)initWithRowCount:(int)rowCount columnCount:(int)columnCount;
#end
So we have a basic constructor which accepts number of rows and number of columns as an input. You might actually need only one parameter. And the implementation:
#import "Matrix.h"
#interface Matrix ()
#property (nonatomic) double *buffer;
#property (nonatomic) double *solvedBuffer; // Not sure what this is and how you wish to serve it
#property (nonatomic) int rowCount;
#property (nonatomic) int columnCount;
#end
#implementation Matrix
- (void)dealloc {
[self freeBuffer];
}
- (instancetype)initWithRowCount:(int)rowCount columnCount:(int)columnCount {
if((self = [super init])) {
self.rowCount = rowCount;
self.columnCount = columnCount;
[self generateBuffer];
}
return self;
}
- (double)elementAt:(int)row column:(int)column {
return self.buffer[column*self.rowCount + row];
}
- (void)setElementAt:(int)row column:(int)column to:(double)value {
self.buffer[column*self.rowCount + row] = value;
}
- (void)generateBuffer {
if(self.rowCount > 0 && self.columnCount > 0) {
int size = sizeof(CGFloat)*self.rowCount*self.columnCount;
self.buffer = malloc(size);
memset(self.buffer, 0, size); // Set it all to zero
}
}
- (void)freeBuffer {
if(self.buffer) {
free(self.buffer);
self.buffer = nil;
}
}
- (void)solveSystem {
self.solvedBuffer = solveSystemOfEquatationWithAugmentedMatrix((double **)self.buffer, self.rowCount);
}
#end
Nothing special really. We have access to buffer, ability to generate it and need to release it manually when done.
I added the "solve system" method which I am not sure what you expect as an output so you will need to create interface for it on your own.
Anyway, if this is an Xcode project you simply create a new file and select to be ObjectiveC. Xcode will ask you to create a bridging header which you need to confirm. Then find this bridging header and add #import "Matrix.h" or whatever your file names will be and this object will be available to you in Swift. As for the C part you can see all should be pretty straight forward. IF you really need to expose C++ then simply rename your Matrix.m to Matrix.mm and Xcode should do the rest for you.
Please also be careful if your matrix should be column or row major. The access may change to self.buffer[row*self.columnCount + column] in the other case in 2 methods.

Related

openCv IOS -No viable Overloaded '='

I am running an example code form a book, it is about using openCv on IOS device to do video processing. but I got "No viable overloaded '='" error, and i did search the StackOverFlow and found some similar posts and answers, but all solutions does not work for me, so I post code as below, and hope anyone can give some suggestions. Really appreciate it!
This is ViewController.h file:
#import <UIKit/UIKit.h>
#import <opencv2/imgcodecs/ios.h>
#import "CvEffects/RetroFilter.hpp"
#import <opencv2/videoio/cap_ios.h>
#interface ViewController : UIViewController<CvVideoCameraDelegate>
{
CvVideoCamera* videoCamera;
BOOL isCapturing;
RetroFilter::Parameters params;
cv::Ptr<RetroFilter> filter;
uint64_t prevTime;
}
#property (nonatomic, strong) CvVideoCamera* videoCamera;
#property (nonatomic, strong) IBOutlet UIImageView* imageView;
#property (nonatomic, strong) IBOutlet UIToolbar* toolbar;
#property (nonatomic, weak) IBOutlet
UIBarButtonItem* startCaptureButton;
#property (nonatomic, weak) IBOutlet
UIBarButtonItem* stopCaptureButton;
-(IBAction)startCaptureButtonPressed:(id)sender;
-(IBAction)stopCaptureButtonPressed:(id)sender;
#end
This is the ViewController.m file:
#import "ViewController.h"
#import <mach/mach_time.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize imageView;
#synthesize startCaptureButton;
#synthesize toolbar;
#synthesize videoCamera;
- (void)viewDidLoad
{
[super viewDidLoad];
// Initialize camera
videoCamera = [[CvVideoCamera alloc]
initWithParentView:imageView];
videoCamera.delegate = self;
videoCamera.defaultAVCaptureDevicePosition =
AVCaptureDevicePositionFront;
videoCamera.defaultAVCaptureSessionPreset =
AVCaptureSessionPreset352x288;
videoCamera.defaultAVCaptureVideoOrientation =
AVCaptureVideoOrientationPortrait;
videoCamera.defaultFPS = 30;
isCapturing = NO;
// Load textures
UIImage* resImage = [UIImage imageNamed:#"scratches.png"];
UIImageToMat(resImage, params.scratches);
resImage = [UIImage imageNamed:#"fuzzy_border.png"];
UIImageToMat(resImage, params.fuzzyBorder);
filter = NULL;
prevTime = mach_absolute_time();
}
- (NSInteger)supportedInterfaceOrientations
{
// Only portrait orientation
return UIInterfaceOrientationMaskPortrait;
}
-(IBAction)startCaptureButtonPressed:(id)sender
{
[videoCamera start];
isCapturing = YES;
params.frameSize = cv::Size(videoCamera.imageWidth,
videoCamera.imageHeight);
if (!filter)
filter = new RetroFilter(params);
}
-(IBAction)stopCaptureButtonPressed:(id)sender
{
[videoCamera stop];
isCapturing = NO;
}
//TODO: may be remove this code
static double machTimeToSecs(uint64_t time)
{
mach_timebase_info_data_t timebase;
mach_timebase_info(&timebase);
return (double)time * (double)timebase.numer /
(double)timebase.denom / 1e9;
}
// Macros for time measurements
#if 1
#define TS(name) int64 t_##name = cv::getTickCount()
#define TE(name) printf("TIMER_" #name ": %.2fms\n", \
1000.*((cv::getTickCount() - t_##name) / cv::getTickFrequency()))
#else
#define TS(name)
#define TE(name)
#endif
- (void)processImage:(cv::Mat&)image
{
cv::Mat inputFrame = image;
BOOL isNeedRotation = image.size() != params.frameSize;
if (isNeedRotation)
inputFrame = image.t();
// Apply filter
cv::Mat finalFrame;
TS(ApplyingFilter);
filter->applyToVideo(inputFrame, finalFrame);
TE(ApplyingFilter);
if (isNeedRotation)
finalFrame = finalFrame.t();
// Add fps label to the frame
uint64_t currTime = mach_absolute_time();
double timeInSeconds = machTimeToSecs(currTime - prevTime);
prevTime = currTime;
double fps = 1.0 / timeInSeconds;
NSString* fpsString =
[NSString stringWithFormat:#"FPS = %3.2f", fps];
cv::putText(finalFrame, [fpsString UTF8String],
cv::Point(30, 30), cv::FONT_HERSHEY_COMPLEX_SMALL,
0.8, cv::Scalar::all(255));
finalFrame.copyTo(image);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if (isCapturing)
{
[videoCamera stop];
}
}
- (void)dealloc
{
videoCamera.delegate = nil;
}
#end
And I got the error at two statements:
filter = NULL;
and
filter = new RetroFilter(params);
First issue, assigning the pointer:
filter = Ptr<RetroFilter>(new RetroFilter(params));
Second issue, Nulling the pointer:
filter = cv::Ptr<RetroFilter>::Ptr();
The reason is that the cv::Ptr object does not have overrides that make this simpler. The standard library's smart pointer class does a much nicer job of being easy to use.
The first issue is that the only = operator provided is this:
Ptr& operator = (const Ptr& ptr);
This means that you can't assign a RetroFilter to it, only another cv::Ptr, so you need to wrap the RetroFilter already.
The second issue, similar to the first, there is no override = operator that takes NULL. The best way to express a null cv::Ptr is
cv::Ptr<RetroFilter>::Ptr();
Which, being an instance of cv::Ptr can be assigned using the '=' operator.
Glad I could help!
Many thanks to #KirkSpaziani.
I figured that following code works, but do not know why?
Try filter = cv::Ptr<RetroFilter>(new RetroFilter(params);
filter = cv::Ptr<RetroFilter>::Ptr()

Why I often got ExcBadAccess in this code:

I uses ARC
NSAttributedString * arString = [self asSchedule:arTimes];
self.lblTimeLeft.attributedText = arString;
Not much information is given. It goes straight to main
int main(int argc, char *argv[])
{
#autoreleasepool {
//int retVal = UIApplicationMain(argc, argv, nil, nil);
int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([newIsiKotaAppDelegate class]));
return retVal;
}
}
I manage to spot that the last code executed is this:
self.lblTimeLeft.attributedText = arString;
I provided additional code to test things out
NSAttributedString * arString = [self asSchedule:arTimes];
self.lblTimeLeft.attributedText = arString;
PO1(#"Hello World");
while(false);// Error happen after this code is executed
}
The code is part of the routine that provides UITableViewCell to display. So error happen after
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger row = indexPath.row;
NSDictionary * rowDictionary = _marManagedObjectArray [row];
BGCinemaScheduleCell * BCS = [[BGCinemaScheduleCell alloc]init];
BCS.dicCinemaDictionary =rowDictionary;
return BCS; //This is where the error happen
}
It seems that the NSAttributedString works fine till it's actually displayed or something.
The error seems to happen on
TAttributes::TAttributes(__CFDictionary const *)
The content of asSchedule is usual
-(NSAttributedString *) asSchedule:(NSArray *) arTimes
{
NSMutableArray * timeBefore = [NSMutableArray array];
NSMutableArray * timeAfter = [NSMutableArray array];
for (NSString * strTime in arTimes) {
NSDate * date = [NSDate dtWithCurrentDateAndProvidedTime: strTime];
NSTimeInterval dblInterval =[date timeIntervalSinceNow];
if (dblInterval>0)
{
[timeAfter addObject:strTime];
}
else{
[timeBefore addObject:strTime];
}
}
NSString * strTimeBefore = [timeBefore componentsJoinedByString:#" "];
NSString * strTimeAfter = [timeAfter componentsJoinedByString:#" "];
NSString *yourString = [NSString stringWithFormat:#"%# %#", strTimeBefore, strTimeAfter];
// start at the end of strTimeBefore and go the length of strTimeAfter
NSRange boldedRange = NSMakeRange([strTimeBefore length] + 1, [strTimeAfter length]);
NSString *boldFontName = [[UIFont boldSystemFontOfSize:12] fontName];
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:yourString];
[attrString addAttribute:NSFontAttributeName value:boldFontName range:boldedRange];
PO1(NSStringFromRange(boldedRange));
PO1(#(attrString.length));
return attrString;
}
Update:
I changed self.lblTimeLeft.attributedText = arString; to self.lblTimeLeft.attributedText = arString.copy; to no avail.
I wonder if this could be the problem:
[attrString addAttribute:NSFontAttributeName value:boldFontName range:boldedRange];
I think the original sample use KCFont something instead of NSFontAttributeName
You were almost there:
I wonder if this could be the problem: [attrString addAttribute:NSFontAttributeName value:boldFontName range:boldedRange];
Your code says:
NSString *boldFontName = [[UIFont boldSystemFontOfSize:12] fontName];
...
[attrString addAttribute:NSFontAttributeName value:boldFontName range:boldedRange];
It should say:
UIFont *boldFont = [UIFont boldSystemFontOfSize:12];
...
[attrString addAttribute:NSFontAttributeName value:boldFont range:boldedRange];
According to Table 1 of the docs (link below) and my own app code, you should be passing an NSFont not a font name.
https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/AttributedStrings/Articles/standardAttributes.html#//apple_ref/doc/uid/TP40004903-SW2
Can't tell from the code whether your project uses ARC or not, but it looks like your asSchedule method returns a string with a refcount of 0. It's up to you to retain a reference to that string, either old-school using the retain keyword (probably a bad idea) or by assigning it to a property declared with the strong keyword (or retain, if pre-ARC).
The best tool for debugging this stuff is Instruments, using the Zombies inspection. If I am right that you are trying to use the string after its refcount hits 0 and the memory has been released, you'll see the history of refcount increments and decrements pretty clearly.
Try alloc and initing the Attributedstring and then assign it to the self.lblTimeLeft.attributedText

Memory leak in obj-c/c++ code

I'm using instruments and I have a lot of memory being leaked by this method. It is used everywhere in my app, and when I built it, I used some c++ (i think).
Please let me know where the leak is happening if possible?
Instruments just says Malloc 16 bytes and has about 60 MB worth of these guys...
/*
Parses a dicionary into an object
*/
+(id)makeObject:(id)object fromDictionary:(NSDictionary *)dict{
#autoreleasepool {
NSArray *keys = #[#"#\"NSMutableArray\""];
//Init result
id result = object;
//Iterate every key
for (id key in [dict allKeys]) {
//Convert key to const char
const char * c = [key cStringUsingEncoding:NSASCIIStringEncoding];
//Use c to see if the class has this property
if (class_getProperty([object class], c)) {
//get the property
objc_property_t property = class_getProperty([result class], c);
//Get the property name and type
const char *name = property_getName(property);
const char *type = property_copyAttributeValue(property, "T");
//Cast const char to string
NSString *pNameString = [NSString stringWithFormat:#"%s",name];
NSString *typeString = [NSString stringWithFormat:#"%s",type];
//Add relationships
if ([keys containsObject:typeString]) {
//Get array of objects
NSArray *relationship = [dict objectForKey:pNameString];
NSMutableArray *allSubObjects = [[NSMutableArray alloc]init];
//Parse each individual object
for (NSDictionary *relationshipObj in relationship) {
//Create class from relationship
Class class = NSClassFromString(pNameString);
//Create object
id sub = [self makeObject:[[class alloc]init] fromDictionary:relationshipObj];
[allSubObjects addObject:sub];
}
[result setValue:allSubObjects forKey:pNameString];
}else{
//If so set the property for the key
[result setValue:[dict objectForKey:key] forKey:key];
}
}else{
//NSLog(#"%# did not respond to : %#", result, key);
}
}
//Return result
return result;
}
}
EDIT:
Instruments shows these two items most likely the culprits. What's the best way to fix this?
//Cast const char to string
NSString *pNameString = [NSString stringWithFormat:#"%s",name];
NSString *typeString = [NSString stringWithFormat:#"%s",type];
1) [NSString cStringUsingEncoding]
This allocate some memory internally which will not freed until the NSString object is deallocated. I don't know exactly but could it be possible repeatedly sending cStringUsingEncoding incur unused memory building up?
2) property_copyAttributeValue
Documentation says,
Return Value A C array of pointers of type objc_property_t describing
the properties declared by proto. Any properties declared by other
protocols adopted by this protocol are not included. The array
contains *outCount pointers followed by a NULL terminator. You must
free the array with free().
You need to free const char *type.
3) NSMutableArray *allSubObjects = [[NSMutableArray alloc]init]
It should be NSMutableArray *allSubObjects = [NSMutableArray array].
4) Object creation
Class class = NSClassFromString(pNameString);
//Create object
id sub = [self makeObject:[[class alloc]init] fromDictionary:relationshipObj];
I'd put this code block in its own autorelease pool.
[class alloc]init] is also suspicious.

UIAutomation testing with Cocos2d, possible?

Is it possible to use UIAutomation with cocos2d or any opengl application for that matter?
Specifically I want to use the zucchini framework to test my cocos2d game but that just uses UIAutomation anyway.
You can create custom steps in Zucchini and specify the coordinates to tap, e.g.
'Choose the red bird' : ->
target.tap({x:278, y:36})
'Press Fire' : ->
target.tap({x:170, y:260})
So, I got started with calabash-iOS and expanding on its backdoor. This is just for starters but with this you can get the accessibility label of the current CCScene, so you can check what screen is currently on and thus use for scripting actions. I'm not used to working with objc runtime, but as you can see it's possible to get properties, methods etc. A bit more digging and it should be possible to wrap more functionality, and hopefully something to wrap the cocos2d CCNode structure as well. This is a work in progress.
To use this you need to install https://github.com/calabash/calabash-ios and then implement the below function in the app delegate. Don't forget to set .accessibilityLabel to something like #"menu", #"game" or similar in your code. Optimally, only for the *-cal target, you don't want this code in production builds.
-(NSString*)calabashBackdoor:(NSString*)aIgnorable {
DLog(#"calabashBackdoor: %#", aIgnorable);
// UIApplication* app = [UIApplication sharedApplication];
if (aIgnorable != nil) {
NSArray* p = [aIgnorable componentsSeparatedByString:#" "];
NSString* command = [p objectAtIndex:0];
if ([command isEqualToString:#"getCurrentSceneLabel"]) {
CCDirector* director = [CCDirector sharedDirector];
DLog(#"director.runningScene.accessibilityLabel: %#", director.runningScene.accessibilityLabel);
return director.runningScene.accessibilityLabel;
}
else if ([command isEqualToString:#"class_copyMethodList"]) {
CCDirector* director = [CCDirector sharedDirector];
id inspectThisObject = director.runningScene;
DLog(#"inspectThisObject: %#, %#", [inspectThisObject class], inspectThisObject);
unsigned int count;
// To get the class methods of a class, use class_copyMethodList(object_getClass(cls), &count).
Method* methods = class_copyMethodList(object_getClass(inspectThisObject), &count);
//NSMutableString* returnstring = [NSMutableString string];
NSMutableArray* arrayOfMethodnames = [NSMutableArray array];
if (methods != NULL) {
for (int i = 0; i < count; i++) {
Method method = methods[i];
NSString* stringMethod = NSStringFromSelector(method_getName(method)); //NSStringFromSelector(method->method_name);
[arrayOfMethodnames addObject:stringMethod];
}
// An array of pointers of type Method describing the instance methods implemented by the class—any instance methods implemented by superclasses are not included. The array contains *outCount pointers followed by a NULL terminator. You must free the array with free().
free(methods);
}
DLog(#"arrayOfMethodnames: %#", arrayOfMethodnames);
return [arrayOfMethodnames componentsJoinedByString:#","];
}
else if ([command isEqualToString:#"class_copyPropertyList"]) {
CCDirector* director = [CCDirector sharedDirector];
id inspectThisObject = director.runningScene;
DLog(#"inspectThisObject: %#, %#", [inspectThisObject class], inspectThisObject);
unsigned int count;
// An array of pointers of type objc_property_t describing the properties declared by the class. Any properties declared by superclasses are not included. The array contains *outCount pointers followed by a NULL terminator. You must free the array with free().
//
// If cls declares no properties, or cls is Nil, returns NULL and *outCount is 0.
//
objc_property_t* properties = class_copyPropertyList(object_getClass(inspectThisObject), &count);
NSMutableArray* arrayOfProperties = [NSMutableArray array];
if (properties != NULL) {
for (int i = 0; i < count; i++) {
objc_property_t property = properties[i];
const char* CCS = property_getName(property);
NSString* str = [NSString stringWithUTF8String:CCS];
[arrayOfProperties addObject:str];
}
free(properties);
}
DLog(#"arrayOfProperties: %#", arrayOfProperties);
return [arrayOfProperties componentsJoinedByString:#","];
}
else {
DLog(#"Unhandled command: %#", command);
}
}
return #"calabashBackdoor nil!";
}
In Prefix.pch put this
#ifdef DEBUG
# define DLog( s, ... ) NSLog( #"<%p %#:(%d)> %#", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
# define DLog(...) /* */
#endif
#define ALog(...) NSLog(__VA_ARGS__)
When you get calabash-ios and running, add this in step_definitions/somesteps.rb:
Then(/^I backdoor (.+)$/) do |x|
backdoor("calabashBackdoor:", x)
end

How do I pass a list of objects from C++ to Lua?

I'm the lead dev for Bitfighter, and am adding user-scripted bots using Lua. I'm working with C++ and Lua using Lunar to glue them together.
I'm trying to do something that I think should be pretty simple: I have an C++ object in Lua (bot in the code below), and I call a method on it that (findItems) which causes C++ to search the area around the robot and return a list of objects it finds (TestItems and others not shown here). My question is simply how do I assemble and return the list of found items in C++, and then iterate over them in Lua?
Basically, I want to fill in the <<<< Create list of items, return it to lua >>>> block below, and make any corrections I may need in the Lua code itself, included below that.
I've tried to keep the code simple but complete. Hope there's not too much here! Thanks!
C++ Header file
class TestItem : public LuaObject
{
public:
TestItem(); // C++ constructor
///// Lua Interface
TestItem(lua_State *L) { } ; // Lua constructor
static const char className[];
static Lunar<TestItem>::RegType methods[];
S32 getClassID(lua_State *L) { return returnInt(L, TestItemType); }
};
class LuaRobot : public Robot
{
LuaRobot(); // C++ constructor
///// Lua Interface
LuaRobot(lua_State *L) { } ; // Lua constructor
static const char className[];
static Lunar<LuaRobot>::RegType methods[];
S32 findItems(lua_State *L);
}
C++ .cpp file
const char LuaRobot::className[] = "Robot"; // Class name in Lua
// Define the methods we will expose to Lua
Lunar<LuaRobot>::RegType LuaRobot::methods[] =
{
method(LuaRobot, findItems),
{0,0} // End method list
};
S32 LuaRobot::findItems(lua_State *L)
{
range = getIntFromStack(L, 1); // Pop range from the stack
thisRobot->findObjects(fillVector, range); // Put items in fillVector
<<<< Create list of items, return it to lua >>>>
for(int i=0; i < fillVector.size(); i++)
do something(fillVector[i]); // Do... what, exactly?
return something;
}
/////
const char TestItem::className[] = "TestItem"; // Class name in Lua
// Define the methods we will expose to Lua
Lunar<TestItem>::RegType TestItem::methods[] =
{
// Standard gameItem methods
method(TestItem, getClassID),
{0,0} // End method list
};
Lua Code
bot = LuaRobot( Robot ) -- This is a reference to our bot
range = 10
items = bot:findItems( range )
for i, v in ipairs( items ) do
print( "Item Type: " .. v:getClassID() )
end
So you need to fill a vector and push that to Lua.
Some example code follows. Applications is a std::list.
typedef std::list<std::string> Applications;
I create a table and fill it with the data in my list.
int ReturnArray(lua_State* L) {
lua_createtable(L, applications.size(), 0);
int newTable = lua_gettop(L);
int index = 1;
Applications::const_iterator iter = applications.begin();
while(iter != applications.end()) {
lua_pushstring(L, (*iter).c_str());
lua_rawseti(L, newTable, index);
++iter;
++index;
}
return 1;
}
This leaves me with an array in the stack. If it were returned to Lua, then I could write the following:
for k,v in ipairs( ReturnArray() ) do
print(v)
end
Of course so far, this just gets me a Lua array of strings. To get an array of Lua objects we just tweak your method a bit:
S32 LuaRobot::findItems(lua_State *L)
{
range = getIntFromStack(L, 1); // Pop range from the stack
thisRobot->findObjects(fillVector, range); // Put items in fillVector
// <<<< Create list of items, return it to lua >>>>
lua_createtable(L, fillVector.size(), 0);
int newTable = lua_gettop(L);
for(int i=0; i < fillVector.size(); i++) {
TestItem* item = fillVector[i];
item->push(L); // put an object, not a string, in Lua array
lua_rawseti(L, newTable, i + 1);
}
return 1;
}
This works perfectly. To clarify to others who are reading this, the method
item->push(L)
is
void push(lua_State *L) { Lunar<TestItem>::push(L, this); }
By encapsulating this in a method, it's possible to make the findItems agnostic to what it's finding.
Thank you for the help!