I'm implementing some fast sort algorithms, and to get best performances, I'd like to use a C++ binding in Objective-C. But I want my algorithms to work with user custom objects and with something similar to a NSComparator ; which type may I use in C++ to use these objects ? May I template a class ?
Please let me know about your best solution to bind custom objects in C++ and to use a NSComparator in C++ ?
Best regards,
Hervé HL.
With the help of #Dave, I finally found the solution to extract pointers from NSArray, no matter the contained type, do some complex operations, and then reassemble the pointers, using void * type.
Just rename your .m file to .mm to enable Objective-C++ features.
void *array[[self count]]; //create an empty void* array
for (uint i = 0; i < [self count]; i++)
{
int address = (int)[self objectAtIndex:i];
array[i] = (void *)address; //insert NSObjects pointers into void* array
}
To get back the NSObjects from void* array, just proceed like this:
NSMutableArray *doneAr = [[NSMutableArray alloc] init]; //create a NSMutableArray
for (uint i = 0; i < [self count]; i++)
{
void *tmp = (void **)done + i; //extract the pointer in the void* array
NSString *strTmp = (NSString *)(*((Class *)tmp)); //convert it back to origin type (here NSString *)
[doneAr addObject:strTmp]; //add it back to the previously created NSMutableArray
}
Hope this answer could be helpful for future issues. If you wish to use NSComparator like me in your C++ implementation, just rename your .m file into .mm file so as you can use Objective-C++ features ; then import and use what you need (NSComparator for example).
Related
I am trying to convert cv::Mat(CV_16UC1) to k4a_image_t. I am trying to do the conversion using this function this function: k4a_image_create_from_buffer.
here is the link: https://microsoft.github.io/Azure-Kinect-Sensor-SDK/master/group___functions_gaf84f2a271bcf6afae429bbccd47071b3.html#gaf84f2a271bcf6afae429bbccd47071b3
so far I have created the buffer data needed to create the image.
std::vector<uchar> array;
if (depth_im.isContinuous())
{
array.assign(depth_im.data, depth_im.data + depth_im.total());
}
else
{
for (int i = 0; i < depth_im.rows; ++i)
{
array.insert(array.end(), depth_im.ptr<uint16_t>(i),
depth_im.ptr<uint16_t>(i) + depth_im.cols);
}
}
uint8_t* b_data = reinterpret_cast<uint8_t*>(&array[0]);
k4a_image_t new_depth_im = NULL;
But I do not understand the parameter 'buffer_release_cb_context'.
Think of it as a pointer to an object that you need when the buffer_release_cb function is called. If you can write that function and free the memory simply based on buffer pointer being passed in then great, you don't need to pass in anything for buffer_release_cb_context and can use NULL instead. Bug if you need the original cv::Mat object, then you should pass that in for buffer_release_cb_context and know that you will get it back as *context in your buffer_release_cb() call.
We would love feedback on how to make the documentation clearer, so feel free to comment on this if you have suggestions.
Here I have a bunch of dispatch_queue_t, and I want to organize them with a NSArray. But as far as I know, there's no such API to do so. So how can i achieve that?
NSArray is not mutable. You must use NSMutableArray, that is subclass of NSArray, to add objects.
And, from iOS 6.0 SDK and the Mac OS X 10.8 SDK, Dispatch Queue is declared as Objective-C types. Thus you can use Dispatch Queue object as Objective-C object.
#import Foundation;
int main()
{
NSMutableArray *array = [[NSMutableArray alloc] init];
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
NSLog(#" q=%#", q);
[array addObject:q];
NSLog(#"array[0]=%#", array[0]);
return 0;
}
Result:
q=<OS_dispatch_queue_root: com.apple.root.default-qos[0x7fff79749b40]>
array[0]=<OS_dispatch_queue_root: com.apple.root.default-qos[0x7fff79749b40]>
This question already has answers here:
What is the meaning of id?
(5 answers)
Closed 9 years ago.
To begin, i'm not very comfortable with Objective c, i'm trying to convert a C++ class into an Objective C and i'm having trouble with the implementation on my class this is what i got for c++
UniqueWord::UniqueWord(const string word, const int line)
{
wordCatalog=word;
count = 0;
addLine(line);
}
//Deconstructor.
UniqueWord::~UniqueWord(void)
{
}
and this is what i got for Objective C
#implementation UniqueWord
-(id)initWithString:(NSString*)str andline:(NSInteger)line{
_wordCatalog=str;
count=0;
addline(line);
return ?//return what? it states (Control reaches end of non-void function)
}
I'm really new to classes in objective c, so i'm also asking for a dumbed down answer to,What in the world is an "id" and how do you use it?
Objective C constructors a little bit different. You should create something like following:
-(instancetype)initWithString:(NSString*)str andline:(NSInteger)line{
self = [super init];
if(self == nil) return nil;
_wordCatalog=str;
count=0;
addline(line);
return self;
}
id is a generic class in Objective-C. This is somewhat similar to void* of C++, but with a lot more support from the execution environment so as to not require much typecasting.
There is no parallel concept in C++: an untyped object reference lets you use objects dynamically, with the specifics of the call checked at runtime, rather than at compile time.
Also note that Objective-C uses initializers instead of constructors. The two serve similar purposes, but are not the same: constructors can operate either together with operator new or separately from it, while initializers can operate only with the method that does allocation. Additionally, an initializer can return a different object in place of the one provided by alloc; constructors cannot do that.
An id is a point to any object in Objective-C. An init method returns an allocated and instantiated object. I would refer you to the Apple docs regarding initializers at https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/Initialization/Initialization.html
In using what the article above says you'll want to write something like.
- (instancetype)initWithString:(NSString *)str andLine:(NSInteger)line {
self = [super init];
if (self) {
_wordCatalog = str;
}
return self;
The typical form (which you should use) for init methods is like this:
- (id)init
{
if ( (self = [super init] ) )
{
[other code you want in the constructor]
}
return self;
}
So for the method you have, it should look something like this:
-(id)initWithString:(NSString*)str andline:(NSInteger)line{
if ( (self = [super init]) )
{
_wordCatalog=str;
count=0;
addline(line);
}
return self;
}
That is, unless the super class has an initWithString:andline: constructor, in which case you would use
if ( (self = [super initWithString:string andline:line) )
as the if statement.
I want to call a method in a COM component from C# using COM interop. This is the methods signature:
long GetPrecursorInfoFromScanNum(long nScanNumber,
LPVARIANT pvarPrecursorInfos,
LPLONG pnArraySize)
and this is sample code (which I checked is really working) to call it in C++:
struct PrecursorInfo
{
double dIsolationMass;
double dMonoIsoMass;
long nChargeState;
long nScanNumber;
};
void CTestOCXDlg::OnOpenParentScansOcx()
{
VARIANT vPrecursorInfos;
VariantInit(&vPrecursorInfos);
long nPrecursorInfos = 0;
m_Rawfile.GetPrecursorInfoFromScanNum(m_nScanNumber,
&vPrecursorInfos,
&nPrecursorInfos);
// Access the safearray buffer
BYTE* pData;
SafeArrayAccessData(vPrecursorInfos.parray, (void**)&pData);
for (int i=0; i < nPrecursorInfos; ++i)
{
// Copy the scan information from the safearray buffer
PrecursorInfo info;
memcpy(&info,
pData + i * sizeof(MS_PrecursorInfo),
sizeof(PrecursorInfo));
}
SafeArrayUnaccessData(vPrecursorInfos.parray);
}
And here's the corresponding C# signature after importing the typelib of the COM component:
void GetPrecursorInfoFromScanNum(int nScanNumber, ref object pvarPrecursorInfos, ref int pnArraySize);
If I'm not mistaken, I need to pass in null for pvarPrecursorInfos to make COM interop marshal it as the expected VT_EMPTY variant. When I'm doing it, I get a SafeArrayTypeMismatchException - not really surprising, looking at how the result is expected to be handled in the sample. So I was trying to use a custom marshaler. Since a cannot alter the component itself, I tried to introduce it this way:
[Guid("06F53853-E43C-4F30-9E5F-D1B3668F0C3C")]
[TypeLibType(4160)]
[ComImport]
public interface IInterfaceNew : IInterfaceOrig
{
[DispId(130)]
int GetPrecursorInfoFromScanNum(int nScanNumber, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshaler))] ref object pvarPrecursorInfos, ref int pnArraySize);
}
The TypeLibType and DispID attribute are the same as in the original version. This works as far as that the MyMarshaller.GetInstance() method is called, but I do not get a callback in MyMarshaller.NativeToManaged. Instead, an access violation is reported. So is this a reliable approach? If yes - how can I make it work? If no: are there any alternatives?
(Just a footnote: in theory I could try to use managed C++ to call the component natively. However, there are lots of other methods in it that work fine with COM interop, so I would very much like to stick with C# if there is any way.)
Since someone asked for it, here's my solution in Managed C++.
array<PrecursorInfo^>^ MSFileReaderExt::GetPrecursorInfo(int scanNumber)
{
VARIANT vPrecursorInfos;
VariantInit(&vPrecursorInfos);
long nPrecursorInfos = -1;
//call the COM component
long rc = pRawFile->GetPrecursorInfoFromScanNum(scanNumber, &vPrecursorInfos, &nPrecursorInfos);
//read the result
//vPrecursorInfos.parray points to a byte sequence
//that can be seen as array of MS_PrecursorInfo instances
//(MS_PrecursorInfo is a struct defined within the COM component)
MS_PrecursorInfo* pPrecursors;
SafeArrayAccessData(vPrecursorInfos.parray, (void**)&pPrecursors);
//now transform into a .NET object
array<PrecursorInfo^>^ infos = gcnew array<PrecursorInfo^>(nPrecursorInfos);
MS_PrecursorInfo currentPrecursor;
for (int i=0; i < nPrecursorInfos; ++i)
{
currentPrecursor = pPrecursors[i];
infos[i] = safe_cast<PrecursorInfo^>(Marshal::PtrToStructure(IntPtr(¤tPrecursor), PrecursorInfo::typeid));
}
SafeArrayUnaccessData(vPrecursorInfos.parray);
return infos;
}
I look at the github code mzLib, which I believe is related to this topic. The code looks good, where it calls
pin_ptr<const wchar_t> wch = PtrToStringChars(path);
I think it may cause some problem, better use
pin_ptr<const wchar_t> pathChar = static_cast<wchar_t*>(System::Runtime::InteropServices::Marshal::StringToHGlobalUni(path).ToPointer());
The code then seems to be worked just fine when compiles. However, it might run in problem when imported as dll. I worked on that by adding a constructor,such as
public ref class ThermoDLLClass
{
public:
ThermoDLLClass();
PrecursorInfo GetPrecursorInfo(int scanNum, String^ path);
};
Then, it seems to get precursorInfo and parameters appropriately.
In my application, i have a mix of C++ and Objective C++ code, at one place, i need to insert pointer of c++ class object to NSMutableArray , but i am getting NSINvalidArgument exception,
Can anyone guide me, how can i insert void pointer to NSMuatableArray
This is what i have tried ,
pArray = [[NSMutableArray alloc]initWithCapacity:myList.size()];
Node *node = myList.getHead();
int idx =0;
void *ptr = nil;
while ( node ) {
[pCTArray insertObject:(NSObject *)node atIndex:idx];
node = node->getNext();
idx++;
}
Is there any otherway to insert it into the MutableArray,
the possible workaround i made is : having store index link this
[myArray insertObject:[NSNumber numberWithInt:idx] atIndex:idx];
and this array i would be using in all NSTable/NSOutliveVIew delegate method , i need to pick the index and get the element from the linklist, but worried because of performance, as too many function call would be needed,
Is this any bug in Cocoa or i am making anything wrong ?
NSArrays expect to hold objective-C objects conforming to the NSObject protocol. If you wish to wrap a pointer, use NSValue:
[myArray insertObject:[NSValue valueWithPointer:node] atIndex:idx];
You are manually responsible for all memory management of that pointer, as objective-C can't reference count it in the usual way.
NSMurableArray only accepts Objective-C objects (hence the argument type of addObject: being id).
Are you on the Mac or iOS? If you're on the Mac, You might want to look at NSPointerArray.
You might also be able to be CFMutableArray with custom callback functions, but I haven't tried this myself.
A third option is NSPointerArray.