Hooooookay so here's another "I just have no idea what's going on" problem:
The first time I make a call to getFullWL() below, I get all my values as expected. Each subsequent call, however, returns nan instead of the true value(-nan(0x400000000) in XCode or something)
Furthermore, if I put my "debug" lines in SFLog the value prints as nan but returns the correct value! If I comment out SFLog (which is just a #define for NSLog depending on a level I set - nothing special), then the value is not caught by isnan() in View but is checked as isnan() in Window
Window and View are sitting on one thread, while DataModule and DCMPix are sitting on another (main thread, I believe). DCMPix also gets some data from a Core-Data object (fImage I believe).
void Window::PopUp()
{
// Grab the View from the Pipe Thread and cast it to my local namespace subclass View
View *view = static_cast< View* >(getPipe()->getView(event.context.view));
float fullww = view->getFullWW();
float fullwl = view->getFullWL();
//SFLog(#"WindowLevel Window %f", wl);
// set SLider Values
if (!isnan(fullwl)){
[[vc winLevel] setMinValue:fullwl];
[[vc winLevel] setMaxValue:(-1.0f*fullwl)];
}
}
float View::getFullWL()
{
float wl = _callPixMethod(#selector(fullwl)).floatValue;
if ( isnan(wl) ) wl = _wl * 2.0f; //<-- is never detected as NaN here
//SFLog(#"WindowLevel View %f", wl); //<-- but is *printed* as NaN here
return wl;
}
// A Union to "cast" the return from the [object performSelector:] method.
// Since it returns `id` and (float)(id) doesn't work.
union performReturn {
id OBJC_ID;
int intValue;
float floatValue;
bool boolValue;
};
performReturn View::_callPixMethod(SEL method)
{
DataModule* data;
DataVisitor getdata(&data);
getConfig()->accept(getdata);
performReturn retVal;
retVal.OBJC_ID = data->callPixMethod(_datasetIndex, _datasetID, method);
return retVal;
}
id DataModule::callPixMethod(int index, std::string predicate, SEL method)
{
DCMPix *pix =[[getSeriesData(predicate) pixList_] objectAtIndex:index];
pthread_mutex_lock(&_mutex);
id retVal = [pix performSelector:method];
pthread_mutex_unlock(&_mutex);
return retVal;
}
// DCMPix.m (from API, can't change)
- (float) fullwl
{
if( fullww == 0 && fullwl == 0) [self computePixMinPixMax];
return fullwl;
}
- (void)computePixMinPixMax
{
float pixmin, pixmax;
if( fImage == nil || width * height <= 0) return;
[checking lock];
#try
{
if( isRGB)
{
pixmax = 255;
pixmin = 0;
}
else
{
float fmin, fmax;
vDSP_minv ( fImage, 1, &fmin, width * height);
vDSP_maxv ( fImage , 1, &fmax, width * height);
pixmax = fmax;
pixmin = fmin;
if( pixmin == pixmax)
{
pixmax = pixmin + 20;
}
}
fullwl = pixmin + (pixmax - pixmin)/2;
fullww = (pixmax - pixmin);
}
#catch (NSException * e)
{
NSLog( #"***** exception in %s: %#", __PRETTY_FUNCTION__, e);
}
[checking unlock];
}
Help! Why would I not be getting the correct value in Window?
I also get no nan's when calling view->getFullWW() even though it follows the same execution path. (but calls #selector(fullww))
No Errors or exceptions are thrown. Just that bizarre behavior.
Thanks!
After further testing, the following change makes all the difference:
float View::getFullWL()
{
float wl = _callPixMethod(#selector(fullwl)).floatValue;
if ( isnan(wl) ) wl = _wl * 2.0f; //<-- is never detected as NaN here
NSLog(#"WindowLevel View %f", wl); //<-- is *printed* as NaN here
return wl; //<-- returns expected value
}
float View::getFullWL()
{
float wl = _callPixMethod(#selector(fullwl)).floatValue;
if ( isnan(wl) ) wl = _wl * 2.0f; //<-- is never detected as NaN here
return wl; //<-- returns `nan`
}
After more testing, it doesn't matter where I put the NSLog() statement, if it occurs before float fullwl = is assigned.
void Window::PopUp()
{
// Grab the View from the Pipe Thread and cast it to my local namespace subclass View
View *view = static_cast< View* >(getPipe()->getView(event.context.view));
float fullww = view->getFullWW();
NSLog("Putting this here, makes fullwl work. Removing it, stores fullwl as nan");
float fullwl = view->getFullWL();
//SFLog(#"WindowLevel Window %f", wl);
Whelp, Here I am answering my own question again but the answer is... RTFM.
The aSelector argument should identify a method that takes no arguments. For methods that return anything other than an object, use NSInvocation.
So the answer is, use NSInvocation and not [obj performSelector:]
Related
I encountered a strange behavior in my C++ program that I don't understand and I don't know how to search for more information. So I ask for advice here hoping someone might know.
I have a class Interface that has a 2 dimensional vector that I initialize in the header :
class Interface {
public:
// code...
const unsigned short int SIZE_X_ = 64;
const unsigned short int SIZE_Y_ = 32;
std::vector<std::vector<bool>> screen_memory_ =
std::vector<std::vector<bool>>(SIZE_X_, std::vector<bool>(SIZE_Y_, false));
// code...
};
Here I expect that I have a SIZE_X_ x SIZE_Y_ vector filled with false booleans.
Later in my program I loop at a fixed rate like so :
void Emulator::loop() {
const milliseconds intervalPeriodMillis{static_cast<int>((1. / FREQ) * 1000)};
//Initialize the chrono timepoint & duration objects we'll be //using over & over inside our sleep loop
system_clock::time_point currentStartTime{system_clock::now()};
system_clock::time_point nextStartTime{currentStartTime};
while (!stop) {
currentStartTime = system_clock::now();
nextStartTime = currentStartTime + intervalPeriodMillis;
// ---- Stuff happens here ----
registers_->trigger_timers();
interface_->toogle_buzzer();
interface_->poll_events();
interface_->get_keys();
romParser_->step();
romParser_->decode();
// ---- ------------------ ----
stop = stop || interface_->requests_close();
std::this_thread::sleep_until(nextStartTime);
}
}
But then during the execution I get a segmentation fault
[1] 7585 segmentation fault (core dumped) ./CHIP8 coin.ch8
I checked with the debugger and some part of the screen_memory_ cannot be accessed anymore. And it seems to happen at random time.
But when I put the initialization of the vector in the constructor body like so :
Interface::Interface(const std::shared_ptr<reg::RegisterManager> & registers, bool hidden)
: registers_(registers) {
// code ...
screen_memory_ =
std::vector<std::vector<bool>>(SIZE_X_, std::vector<bool>(SIZE_Y_, false));
// code ...
}
The segmentation fault doesn't happen anymore. So the solution is just to initialize the vector in the constructor body.
But why ? what is happening there ?
I don't understand what I did wrong, I'm sure someone knows.
Thanks for your help !
[Edit] I found the source of the bug (Or at least what to change so it doesnt give me a segfault anymore).
In my class Interface I use the SDL and SDL_audio libraries to create the display and the buzzer sound. Have a special look where I set the callback want_.callback, the callback Interface::forward_audio_callback and Interface::audio_callback. Here's the code :
// (c) 2021 Maxandre Ogeret
// Licensed under MIT License
#include "Interface.h"
Interface::Interface(const std::shared_ptr<reg::RegisterManager> & registers, bool hidden)
: registers_(registers) {
if (SDL_Init(SDL_INIT_AUDIO != 0) || SDL_Init(SDL_INIT_VIDEO) != 0) {
throw std::runtime_error("Unable to initialize rendering engine.");
}
want_.freq = SAMPLE_RATE;
want_.format = AUDIO_S16SYS;
want_.channels = 1;
want_.samples = 2048;
want_.callback = Interface::forward_audio_callback;
want_.userdata = &sound_userdata_;
if (SDL_OpenAudio(&want_, &have_) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to open audio: %s", SDL_GetError());
}
if (want_.format != have_.format) {
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to get the desired AudioSpec");
}
window = SDL_CreateWindow("CHIP8", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SIZE_X_ * SIZE_MULTIPLIER_, SIZE_Y_ * SIZE_MULTIPLIER_,
hidden ? SDL_WINDOW_HIDDEN : 0);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
bpp_ = SDL_GetWindowSurface(window)->format->BytesPerPixel;
SDL_Delay(1000);
// screen_memory_ = std::vector<std::vector<bool>>(SIZE_X_, std::vector<bool>(SIZE_Y_, false));
}
Interface::~Interface() {
SDL_CloseAudio();
SDL_DestroyWindow(window);
SDL_Quit();
}
// code ...
void Interface::audio_callback(void * user_data, Uint8 * raw_buffer, int bytes) {
audio_buffer_ = reinterpret_cast<Sint16 *>(raw_buffer);
sample_length_ = bytes / 2;
int & sample_nr(*(int *) user_data);
for (int i = 0; i < sample_length_; i++, sample_nr++) {
double time = (double) sample_nr / (double) SAMPLE_RATE;
audio_buffer_[i] = static_cast<Sint16>(
AMPLITUDE * (2 * (2 * floor(220.0f * time) - floor(2 * 220.0f * time)) + 1));
}
}
void Interface::forward_audio_callback(void * user_data, Uint8 * raw_buffer, int bytes) {
static_cast<Interface *>(user_data)->audio_callback(user_data, raw_buffer, bytes);
}
}
In the function Interface::audio_callback, replacing the class variable assignation :
sample_length_ = bytes / 2;
By an int creation and assignation :
int sample_length = bytes / 2;
which gives :
void Interface::audio_callback(void * user_data, Uint8 * raw_buffer, int bytes) {
audio_buffer_ = reinterpret_cast<Sint16 *>(raw_buffer);
int sample_length = bytes / 2;
int &sample_nr(*(int*)user_data);
for(int i = 0; i < sample_length; i++, sample_nr++)
{
double time = (double)sample_nr / (double)SAMPLE_RATE;
audio_buffer_[i] = (Sint16)(AMPLITUDE * sin(2.0f * M_PI * 441.0f * time)); // render 441 HZ sine wave
}
}
The class variable sample_length_ is defined and initialized as private in the header like so :
int sample_length_ = 0;
So I had an idea and I created the variable sample_length_ as public and it works ! So the problem was definitely a scope problem of the class variable sample_length_. But it doesn't explain why the segfault disappeared when I moved the init of some other variable in the class constructor... Did I hit some undefined behavior with my callback ?
Thanks for reading me !
I made a QT Test Application (an app with a GUI that tests some functions I made) for my work project but my exceptions don't work at all and I don't understand why. Maybe I'm missing something but the code seems correct to me, here's a sample:
The function that throws the exception in (with my test it throws std::invalid_argument):
std::vector<pcl::PointCloud<pcl::PointXYZRGB>::Ptr> cloud_manip::fragment_cloud(
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_ptr, float max_scaled_fragment_depth)
{
if (!cloud_ptr)
{
throw invalid_cloud_pointer();
}
if ((aux::cmp_floats(max_scaled_fragment_depth, 0.00, 0.005)) || (max_scaled_fragment_depth < 0))
throw std::invalid_argument("Invalid max fragment depth.");
float curr_depth = FLT_MAX;
std::vector<pcl::PointCloud<pcl::PointXYZRGB>::Ptr> cloud_fragments;
for (unsigned int cloud_it = 0; cloud_it < cloud_ptr->points.size(); cloud_it++)
{
// end of a fragment
if ((cloud_ptr->points[cloud_it].y > (curr_depth + max_scaled_fragment_depth))
|| (cloud_ptr->points[cloud_it].y < (curr_depth - max_scaled_fragment_depth)) )
{
curr_depth = cloud_ptr->points[cloud_it].y;
pcl::PointCloud<pcl::PointXYZRGB>::Ptr new_cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
cloud_fragments.push_back(new_cloud);
}
// filling current cloud
else
(cloud_fragments.back())->points.push_back(cloud_ptr->points[cloud_it]);
}
return cloud_fragments;
}
The first function that catches the exception:
pcl::PointCloud<pcl::PointXYZRGB>::Ptr fast_normal_estimation(pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_ptr, int max_neighbs,
float radius, float x_scale, float y_scale, float z_scale, float max_fragment_depth)
{
try
{
// the cloud colored by its normal vectors; return value
pcl::PointCloud<pcl::PointXYZRGB>::Ptr colored_cloud_ptr;
float max_scaled_fragment_depth = max_fragment_depth / y_scale;
cloud_manip::scale_cloud(cloud_ptr, x_scale, y_scale, z_scale); // scaling cloud
std::vector<pcl::PointCloud<pcl::PointXYZRGB>::Ptr> cloud_fragments =
cloud_manip::fragment_cloud(cloud_ptr, max_scaled_fragment_depth); // fragmenting cloud for less execution time
// estimating the normals for each cloud fragment in parallel
// #pragma omp parallel for schedule(static)
for (unsigned int i = 0; i < cloud_fragments.size(); i++)
{
normal_estimation(cloud_fragments[i], radius, max_neighbs);
}
colored_cloud_ptr = cloud_manip::merge_clouds(cloud_fragments); // merging fragments to build original cloud
cloud_manip::scale_cloud(colored_cloud_ptr, (1.0/x_scale), (1.0/y_scale), (1.0/z_scale)); // restoring widop scale
return colored_cloud_ptr;
}
catch (const std::logic_error& le)
{
throw le;
}
}
The test function:
void test_normal_estimation(std::string import_path, std::string export_path, float radius,
int max_neighbs, float x_scale, float y_scale, float z_scale,
float max_fragment_depth)
{
try
{
pcl::PointCloud<pcl::PointXYZRGB>::Ptr base_cloud;
pcl::PointCloud<pcl::PointXYZRGB>::Ptr colored_cloud; // output cloud
base_cloud = cloud_io::import_cloud(import_path);
colored_cloud = fast_normal_estimation(base_cloud, max_neighbs, radius, x_scale, y_scale, z_scale,
max_fragment_depth);
cloud_io::export_cloud(export_path + "normal_estimation_test_" + boost::lexical_cast<std::string>(radius) + "_"
+ boost::lexical_cast<std::string>(max_neighbs) + "_" + boost::lexical_cast<std::string>(x_scale) + "_"
+ boost::lexical_cast<std::string>(y_scale) + "_" + boost::lexical_cast<std::string>(z_scale) + "_"
+ boost::lexical_cast<std::string>(max_fragment_depth) + ".txt", colored_cloud);
}
catch(const std::logic_error& le)
{
throw le;
}
}
And lastly the interface which is supposed to show an error message:
void normal_estimation_test_form::on_launch_test_btn_clicked()
{
// for when the test is done
QMessageBox done;
this->setEnabled(false);
_ned->radius = ui->radius_dsb->value();
_ned->max_neighbs = ui->max_neighbs_sb->value();
_ned->x_scale = ui->x_scale_dsb->value();
_ned->y_scale = ui->y_scale_dsb->value();
_ned->z_scale = ui->z_scale_dsb->value();
_ned->max_fragment_depth = ui->max_fragm_depth_sb->value();
try
{
test_normal_estimation(_ned->cloud_in_path, _ned->cloud_out_path, _ned->radius,
_ned->max_neighbs, _ned->x_scale, _ned->y_scale,
_ned->z_scale, _ned->max_fragment_depth);
done.setText("Cloud normal estimation test completed.");
done.exec();
}
catch (const std::logic_error& le)
{
QErrorMessage q_err_msg;
QString err_msg;
err_msg.append("Invalid input.");
q_err_msg.showMessage(err_msg, "Input Error");
}
}
Any idea why my exception doesn't get caught at all? Thank you in advance.
edit_1: I know I'm not catching std::invalid_argument but that's because it's a sub-class of std::logic_error according to cplusplus.
I have found an answer to the problem and it is explained here: exception handling in Qt. It doesn't look like a legitimate way to do it but I don't know of any better.
I had to plug into a pre-existing software, managing ASIO audio streams, a simple VST host. Despite of lack of some documentation, I managed to do so however once I load the plugin I get a badly distorted audio signal back.
The VST I'm using works properly (with other VST Hosts) so it's probably some kind of bug in the code I made, however when I disable the "PROCESS" from the plugin (my stream goes through the plugin, it simply does not get processed) it gets back as I sent without any noise or distortion on it.
One thing I'm slightly concerned about is the type of the data used as the ASIO driver fills an __int32 buffer while the plugins wants some float buffer.
That's really depressing as I reviewed zillions of times my code and it seems to be fine.
Here is the code of the class I'm using; please note that some numbers are temporarily hard-coded to help debugging.
VSTPlugIn::VSTPlugIn(const char* fullDirectoryName, const char* ID)
: plugin(NULL)
, blocksize(128) // TODO
, sampleRate(44100.0F) // TODO
, hostID(ID)
{
this->LoadPlugin(fullDirectoryName);
this->ConfigurePluginCallbacks();
this->StartPlugin();
out = new float*[2];
for (int i = 0; i < 2; ++i)
{
out[i] = new float[128];
memset(out[i], 0, 128);
}
}
void VSTPlugIn::LoadPlugin(const char* path)
{
HMODULE modulePtr = LoadLibrary(path);
if(modulePtr == NULL)
{
printf("Failed trying to load VST from '%s', error %d\n", path, GetLastError());
plugin = NULL;
}
// vst 2.4 export name
vstPluginFuncPtr mainEntryPoint = (vstPluginFuncPtr)GetProcAddress(modulePtr, "VSTPluginMain");
// if "VSTPluginMain" was not found, search for "main" (backwards compatibility mode)
if(!mainEntryPoint)
{
mainEntryPoint = (vstPluginFuncPtr)GetProcAddress(modulePtr, "main");
}
// Instantiate the plugin
plugin = mainEntryPoint(hostCallback);
}
void VSTPlugIn::ConfigurePluginCallbacks()
{
// Check plugin's magic number
// If incorrect, then the file either was not loaded properly, is not a
// real VST plugin, or is otherwise corrupt.
if(plugin->magic != kEffectMagic)
{
printf("Plugin's magic number is bad. Plugin will be discarded\n");
plugin = NULL;
}
// Create dispatcher handle
this->dispatcher = (dispatcherFuncPtr)(plugin->dispatcher);
// Set up plugin callback functions
plugin->getParameter = (getParameterFuncPtr)plugin->getParameter;
plugin->processReplacing = (processFuncPtr)plugin->processReplacing;
plugin->setParameter = (setParameterFuncPtr)plugin->setParameter;
}
void VSTPlugIn::StartPlugin()
{
// Set some default properties
dispatcher(plugin, effOpen, 0, 0, NULL, 0);
dispatcher(plugin, effSetSampleRate, 0, 0, NULL, sampleRate);
dispatcher(plugin, effSetBlockSize, 0, blocksize, NULL, 0.0f);
this->ResumePlugin();
}
void VSTPlugIn::ResumePlugin()
{
dispatcher(plugin, effMainsChanged, 0, 1, NULL, 0.0f);
}
void VSTPlugIn::SuspendPlugin()
{
dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0.0f);
}
void VSTPlugIn::ProcessAudio(float** inputs, float** outputs, long numFrames)
{
plugin->processReplacing(plugin, inputs, out, 128);
memcpy(outputs, out, sizeof(float) * 128);
}
EDIT: Here's the code I use to interface my sw with the VST Host
// Copying the outer buffer in the inner container
for(unsigned i = 0; i < bufferLenght; i++)
{
float f;
f = ((float) buff[i]) / (float) std::numeric_limits<int>::max()
if( f > 1 ) f = 1;
if( f < -1 ) f = -1;
samples[0][i] = f;
}
// DO JOB
for(auto it = inserts.begin(); it != inserts.end(); ++it)
{
(*it)->ProcessAudio(samples, samples, bufferLenght);
}
// Copying the result back into the buffer
for(unsigned i = 0; i < bufferLenght; i++)
{
float f = samples[0][i];
int intval;
f = f * std::numeric_limits<int>::max();
if( f > std::numeric_limits<int>::max() ) f = std::numeric_limits<int>::max();
if( f < std::numeric_limits<int>::min() ) f = std::numeric_limits<int>::min();
intval = (int) f;
buff[i] = intval;
}
where "buff" is defined as "__int32* buff"
I'm guessing that when you call f = std::numeric_limits<int>::max() (and the related min() case on the line below), this might cause overflow. Have you tried f = std::numeric_limits<int>::max() - 1?
Same goes for the code snippit above with f = ((float) buff[i]) / (float) std::numeric_limits<int>::max()... I'd also subtract one there to avoid a potential overflow later on.
I am using this method in a Cocos2d X game.
void OpponentNode::discard(int cardNum)
{
log("\nOpponentNode::discard <%d>\n", cardNum);
for (int i = 0; i < vecOpponentHand.size(); i++)
{
if (vecOpponentHand.at(i) == cardNum)
{
vecOpponentHand.erase(vecOpponentHand.begin() + i);
break;
}
}
CardSprite * discardedCard;
for (int i = 0; i < vecOpponentCards.size(); i++)
{
if (vecOpponentCards.at(i)->getTag() == cardNum)
{
discardedCard = vecOpponentCards.at(i);
vecOpponentCards.erase(vecOpponentCards.begin() + i);
break;
}
}
log("\nOpponentNode::discard <%d>\n", cardNum);
discardedCard->makeFaceUp();
RotateTo * rotate = RotateTo::create(0.4 * SPEED_MULTIPLIER, 0);
MoveTo * move = MoveTo::create(0.4 * SPEED_MULTIPLIER,
origin + Vec2(visibleSize.width * 0.75, visibleSize.height * 0.6));
Spawn * spawn = Spawn::create(rotate, move, NULL);
CallFunc * callFunc = CallFunc::create(
[&]()
{
log("\nOpponentNode::discard <%d>\n", cardNum); //this one shows garbage/different value
if (delegate)
{
delegate->opponentNodeDidFinishDiscard(this, cardNum);
}
this->removeChild(discardedCard);
});
discardedCard->runAction(Sequence::create(spawn, callFunc, NULL));
log("\nOpponentNode::discard <%d>\n", cardNum);
}
Strangely, when I log the integer cardNum like above, I get different value from the log inside the lambda function. For example, I get "OpponentNode::discard <402>" from the top 2 logs and the bottom most log but get "OpponentNode::discard <64>" from the log inside the lambda function.
Other points:
The lambda block is executed last.
I mostly get values like 64 or garbage values like -15493456.
My guess is the integer cardNum is getting deallocated before the execution. Can anyone point me to the right direction?
You're capturing a reference to the cardNum parameter. I would think you want to capture that one by value.
It's not clear to me what delegate is. Assuming it's a class member then I think you just need [this, discardedCard, cardNum]. Which you could abbreviate to just [=], although I think the explicit one is clearer.
I think I'm making just a fundamental mistake, but I cannot for the life of me see it.
I'm calling a method on an Objective-C object from within a C++ class (which is locked). I'm using NSInvocation to prevent me from having to write hundreds methods just to access the data in this other object.
These are the steps I'm going through. This is my first call, and I want to pass s2. I can't really provide a compilable example, but hopefully it's just a DUHRRRRR problem on my part.
float s2[3];
id args2s[] = {(id)&_start.x(),(id)&_start.y(),(id)&s2};
_view->_callPixMethod(#selector(convertPixX:pixY:toDICOMCoords:),3,args2s);
This is the View method being called
invokeUnion View::_callPixMethod(SEL method, int nArgs, id args[])
{
DataModule* data;
DataVisitor getdata(&data);
getConfig()->accept(getdata);
invokeUnion retVal;
retVal.OBJC_ID = data->callPixMethod(_index, _datasetKey, method, nArgs, args);
return retVal;
}
Invoke Union is a union so I can get the float value returned by NSInvocation.
union invokeUnion {
id OBJC_ID;
int intValue;
float floatValue;
bool boolValue;
};
This is the method in the data Object (pthread locked with lock() and unlock());
id DataModule::callPixMethod(int index, std::string predicate, SEL method, int nArgs, id args[] )
{
// May Block
DCMPix *pix =[[getSeriesData(predicate) pix] objectAtIndex:index];
lock();
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMethodSignature *signature;
NSInvocation *invocation;
signature = [DCMPix instanceMethodSignatureForSelector:method];
invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:method];
[invocation setTarget:pix];
if (nArgs > 0) for (int n = 0; n < nArgs; n++) {
SFLog(#"invocation: i=%d, *ptr=0x%x, valf=%f, vald=%d",n,args[n],*args[n],*args[n]);
[invocation setArgument:args[n] atIndex:2+n];
}
id retVal;
[invocation invoke];
[invocation getReturnValue:&retVal];
[pool release];
unlock();
return retVal;
}
The method in the DCMPix object (which I can't modify, it's part of a library) is the following:
-(void) convertPixX: (float) x pixY: (float) y toDICOMCoords: (float*) d pixelCenter: (BOOL) pixelCenter
{
if( pixelCenter)
{
x -= 0.5;
y -= 0.5;
}
d[0] = originX + y*orientation[3]*pixelSpacingY + x*orientation[0]*pixelSpacingX;
d[1] = originY + y*orientation[4]*pixelSpacingY + x*orientation[1]*pixelSpacingX;
d[2] = originZ + y*orientation[5]*pixelSpacingY + x*orientation[2]*pixelSpacingX;
}
-(void) convertPixX: (float) x pixY: (float) y toDICOMCoords: (float*) d
{
[self convertPixX: x pixY: y toDICOMCoords: d pixelCenter: YES];
}
It's crashing when it tries to access d[0]. BAD_EXC_ACCESS which I know means it's accessing released memory, or memory outside of it's scope.
I'm getting lost keeping track of my pointers to pointers. the two float values come across fine (as does other info in other methods) but this is the only one asking for a float* as a parameter. From what I understand the convertPixX: method was converted over from a C program written for Mac OS 9... which is why it asks for the c-array as an out value... I think.
Anyway, any insight would be greatly appreciated.
I've tried sending the value like this:
float *s2 = new float[3];
void* ps2 = &s2;
id args2s[] = {(id)&_start.x(),(id)&_start.y(),(id)&ps2};
_view->_callPixMethod(#selector(convertPixX:pixY:toDICOMCoords:),3,args2s);
But that gives a SIGKILL - plus I'm sure it's bogus and wrong. ... but I tried.
anyway... pointers! cross-language! argh!
Thanks,
An array is not a pointer. Try adding the following line
NSLog(#"%p, %p", s2, &s2);
just above.
id args2s[] = {(id)&_start.x(),(id)&_start.y(),(id)&s2};
s2 and &s2 are both the address of the first float in your array, so when you do:
[invocation setArgument:args[n] atIndex:2+n];
for n = 2, you are not copying in a pointer to the first float, but the first float, possibly the first two floats if an id is 64 bits wide.
Edit:
To fix the issue, this might work (not tested).
float s2[3];
float* s2Pointer = s2;
id args2s[] = {(id)&_start.x(),(id)&_start.y(),(id)&s2Pointer};
_view->_callPixMethod(#selector(convertPixX:pixY:toDICOMCoords:),3,args2s);
s2Pointer is a real pointer that will give you the double indirection you need.