I'm working on a native C++ application, and I am trying to utilize some managed classes from a separate assembly to read/write data containers that were originally developed in C#. The interop layer is basically loading the data, and then mirroring the managed data into a functionally equivalent native data container for use by the application (and then obviously back again for writing).
To be totally honest, it's not been super fun trying to do this interop, but it's mostly working at this point. There's one step of the translation that's holding everything up, though, and while debugging it, a managed exception (System.NullReferenceException) is being thrown on a line that is native C++ only. I don't understand at all why this is happening, and I'm hoping that someone can make sense of what's going on. I wouldn't even have expected it to be in a managed portion of the stack at the location where it's throwing...
This is a stripped down version of what our code is doing:
// The native data types.
class cUnmanagedInterpolator
{
virtual std::vector<std::pair<double, cUnmanagedInterpolator>& GetPoints() { return mPoints; }
std::vector<std::pair<double, cUnmanagedInterpolator> mPoints;
};
class cUnmanagedInterpolator_Const : public cUnmanagedInterpolator
{
virtual double GetValue() const { return mValue; }
double mValue
};
// The managed data types (please forgive syntax errors here; they're actually written in C# in our software, and this is just to get the point across).
class ManagedInterpolator
{
property List<KeyValuePair<double, ManagedInterpolator^>^ Points = gcnew List<KeyValuePair<double, ManagedInterpolator^>();
};
class ManagedInterpolator_Const : public ManagedInterpolator
{
property double DependentValue;
};
// The function to mirror the managed data container into a native counterpart.
void CopyManagedPoints( ManagedInterpolator^ rhManagedInterpolator, cUnmanagedInterpolator* pUnmanagedInterpolator )
{
// Go through each managed point in the interpolator and add a corresponding unmanaged one.
for each( auto point in rhManagedContainer->Points )
{
// If this is a constant interpolator, just copy the values.
if( dynamic_cast<ManagedInterpolator_Const^>( point->Value ) != nullptr )
{
// Create a new unmanaged copy of the point.
// I even tried making x and r pointers and allocating the doubles on the heap with "new" to make sure they weren't somehow ending up as CLI types, but it didn't make a difference.
double x = point->Key;
double r = dynamic_cast<ManagedInterpolator_Const^>( point->Value )->DependentValue;
std::pair<double, cUnmanagedInterpolator> newPoint( x, cUnmanagedInterpolator_Const( r ) );
// The unmanaged point data was looking weird, and this appeared to be where it was happening, so create a message with what it thinks the point is at this point.
// ***The next line is where the System.NullReferenceException is thrown.***
std::string debugMessage = MakeString( newPoint.first ) + ", " + MakeString( dynamic_cast<cUnmanagedInterpolator_Const*>( &( newPoint.second ) )->GetValue() );
// Add the copy to the unmanaged interpolator.
pUnmanagedInterpolator->GetPoints().push_back( newPoint );
// ***Trying to reference the newly created point by using pUnmanagedInterpolator->GetPoints().back() also results in an exception.
// Show the debug message to the user.
AfxMessageBox( debugMessage.c_str() );
}
// Otherwise, add a new base class interpolator.
else
{
cUnmanagedInterpolator* pNewInterp = new cUnmanagedInterpolator();
// Recurse as deep as it goes.
if( pNewInterp )
{
pUnmanagedInterpolator->GetPoints().push_back( std::make_pair( point->Key, std::move( *pNewInterp ) ) );
CopyManagedPoints( point->Value, &( pUnmanagedInterpolator->GetPoints().back().second ) );
delete pNewInterp;
pNewInterp = nullptr;
}
}
}
}
// Roughly how the function would be used.
int main()
{
ManagedInterpolator^ rhManagedInterpolator = gcnew ManagedInterpolator( /*initialization information*/ );
cUnmanagedInterpolator* pNewInterp = new cUnmanagedInterpolator();
CopyManagedPoints( rhManagedInterpolator, pNewInterp );
// Use the data...
return 0;
}
The exception occurs in the inner if statement (there are comments preceded by three asterisks ("***") marking where the problems appear to be occurring).
A quick summary of the code in case it's not clear enough:
Basically, the data container is an "interpolator" that contains pairs of independent and dependent values ("points"). The dependent values can be either another interpolator or a scalar value. Dependent values that are interpolators can recurse as deeply as desired, but must end in a fixed value, which is a class derived from the interpolator.
You're using handles (^) so this is not native code. The problem is that your pair newPoint has a cUnmanagedInterpolator object as its second value, which you try to dynamic_cast to a cUnmanagedInterpolator_Const on the line that generates the NullReferenceException. This cast will fail and return a nullptr, which when dereferenced will cause the exception.
Fundamentally, while you start with a cUnmanagedInterpolator_Const, it is sliced to a cUnmanagedInterpolator when you create the pair, losing its identity as a cUnmanagedInterpolator_Const and becoming a cUnmanagedInterpolator.
I have been struggling with this problem for a few hours now and I've searched for every term that made sense to me. I might even have already read through a relevant answer but didn't recognize it as such because I'm a little confused by pointers.
So, I have a struct that is part of an object which is part of a chain of objects that is "anchored" (if you can call it that) in another object, which is itself part of a chain which is "anchored" in an anchor object initialized in main.
struct values
{
double val, ues;
}
class small
{
public:
values vals;
}
class big
{
public:
small *small_anchor;
}
values &getPointerToStruct(big *con)
{
values *return_vals;
if(con->small_anchor->vals.val==10)
return_vals=con->small_anchor->vals;
return (&return_vals);
}
int main()
{
values *main_values=NULL;//This is supposed to be pointing to the "vals" struct contained inside the "small" object.
big *big_anchor;
big_anchor = new big;
big_anchor->small_anchor = new small;
big_anchor->small_anchor->vals.val=10;
big_anchor->small_anchor->vals.ues=5;
main_values = getPointerToStruct(&big_anchor);//now main_values should be pointing to the "vals" struct
//I want to manipulate the value INSIDE the object itself
main_values.val++;
}
I have tried every combination of &, * and no prefix I could come up with, but nothing would give the result I was hoping for. "Closest" I got was copying "vals" (from inside the object) into "main_values" so that I could manipulate the values there, which isn't of any use to me, as I want to manipulate the SOURCE of the variables.
Also please note that I left out the process of scrolling down the chains to get to the object I was trying to reach and that "return_vals" seems arbitrary, but it happens that there are two possible candidates that could be the struct my program is looking for and the "return_vals" contains the best candidate found so far and is overwritten when a better one is found, or returned when the routine ends.
You wrote
values &getPointerToStruct(big *con)
{
values *return_vals;
if(con->small_anchor->vals.val==10)
return_vals=con->small_anchor->vals;
return (&return_vals);
}
where I think you wanted
values *getPointerToStruct(big *con)
{
values *return_vals = 0;
if(con->small_anchor->vals.val==10)
return_vals=&con->small_anchor->vals;
return (return_vals);
}
But the . in main_values.val++; is wrong anyway. You need to be consistent about whether you wanted to work with a values* or a values&. You could make all this work with a values& if you like, but then main_values should be a values& and not declared until you are ready to initialize it. return_vals would not be practical as a values& because of the stated requirement that the full code gives it a tentative address that is conditionally modified later. But even with return_vals as a values* you could still return a values& if that is what you preferred.
I encountered a problem when debugging my model (written in C++) in Eclipse CDT. The problem is that when I pass a structure variable, who contains various member variables such as string or vector, to a function by reference, the value of certain member variables are not updated in the scope of that function. More details are provided as below:
struct ModelConfig {
//... here are some other variables and constructors
vector<int> crop_list;
string path_to_input;
//....
};
Say now I start debugging in GDB, and here is the first function call :
void modelMain::setupModel( const ModelConfig & sim_setting ){
//... some operations to configure the model using 'sim_setting'
/* 1.3 - Initialize the land */
Set_Environment(k_farm_land, sim_setting);
// breakpoint here, printing out the value of 'sim_etting' shows 'sim_setting.path_to_input = "data/"' ; Then I enter into 'Set_Environment' function ...
//...
}
void Set_Environment(vector<Soil> & farm_land, const ModelConfig & sim_setting) {
int EXP_ID = sim_setting.EXP_ID;
string strTmp_a;
strTmp_a = sim_setting.path_to_input + "soil/parameters.txt"; // Problem is here: the GDB shows here that sim_setting.path_to_input = " ". I am expecting strTmp_a = "data/soil/parameters.txt" which now is "soil/parameters.txt" ;
//... operations for reading data
}
The sim_setting.path_to_input variable should hold the string value named data/, which is correct during the call in setupModel(...), but the value is lost (or the address is changed actually) during the call in Set_Environment(...)...
When using the GDB debug in Eclipse to trace the address of the variables, I notice that the address of sim_setting seems correct in both setupModel and Set_Environment, but the member variable of path_to_input and crop_list changed into other place, which cause the lost of data. The value of crop_list is created using .push_back().
I did not get the point since I am passing the variable by reference. The only thing that I can imagine is due to the value assignment of string and vector. Anyone have theory for this ? Thank you very much in advance !
I am working with the Laurena library for C++ to add serialization to JSON to my Node Addon. When I initialize the library, it gets to a particular point in the code where it defines two unordered_map objects. They aren't initialized, but instead immediately used (as in the code below). ANY access to any data or any methods within the unordered_maps causes a vector subscript out of range failure.
But ONLY in nodejs.
If I pull the addon code and dump it into a Visual Studio 2013 C++ Console application, without ANY changes, it runs perfectly. Can anyone point me in the direction of what it is about these unodered_maps that isn't supported in node that is in a regular console app?
using namespace laurena;
std::unordered_map<std::string, const descriptor*> classes::_classes_by_name;
std::unordered_map<size_t, const descriptor*> classes::_classes_by_typeid;
void classes::add(const descriptor* myClass)
{
for(int i = 0; i< _classes_by_typeid.size(); i++)
{
printf("in array I (%d) : %Iu", i, _classes_by_typeid[i]); //FAILS!
}
// also failes
printf("Access ANYTHING? %s \n", _classes_by_typeid.hash_function());
// Doesn't fail? WTF??
printf("Post Set array size :: %d\n", _classes_by_name.size());
printf("Post Set array size :: %d\n", _classes_by_typeid.size());
}
I'm Laurena's author.
Laurena's library in this current version has a big flaw as it use global static variables to store classes descriptors. A better implementation would have been to store them into a singleton initialized dynamically.
A possible explain is you call laurena::classes::add from another library's static member/global data constructor. Static / global datas constructors are executed before int main (...)
In this case, if your data's constructor is called before laurena's static maps constructors, then yes you can have the error you describe. See
What’s the “static initialization order fiasco”? at https://isocpp.org/wiki/faq/ctors#static-init-order for more details about this problem.
Then there is two options:
1) laurena::classes static datas must be wrapped into a singleton dynamically created.
laurena::classes::add method should looks then
void classes::add(const descriptor* myClass) // classes::add is a static class function
{
classes* p = classes::get_or_create_instance ();
p->_classes_by_name[myClass->name()] = myClass;
p->_classes_by_typeid[std::type_index(myClass->type()).hash_code()] = myClass;
}
2) Move calls to classes::add into int main ( ... ) :
int main ()
{
// laurena's initialization
laurena::classes::init();
// let's declare TheNerd serializables classes :
declare_TheNerd_classes();
...
}
If you can't use option 2, option 1 is something i could fix.
I need to define a vector of testmodes that I can then manipulate the values of through various methods.
Here is how I define the TestMode class:
class TestMode{
public:
TestMode(int val, int jamramaddr){
value=val;
jamramaddress=jamramaddr;
}
int getAddr(void){
return jamramaddress;
}
void setValue(int val){
value=val;
}
int getValue(void){
return value;
}
private:
int value;
int jamramaddress;
};
Pretty simple.
I then have a TestModeGroup class to perform actions on the vector of testmodes that I have created. That class looks like this:
class TestModeGroup{
public:
TestModeGroup(const std::vector<TestMode> &TestModes){
TestModeVector=TestModes;
}
//Compare the given jamramaddress against the known jamramaddress of the given testmode. If it is a match then update the testmode value
void compareAndStore(TestMode &TM){
int TM_address=TM.getAddr();
if(TM_address==JamRamAddress){
output("Match found! Old TM value %d", TM.getValue());
TM.setValue(JamRamData);
output("New TM value %d", TM.getValue());
}
}
//Commit the given testmode to the jamram with the latest known value
void writeTmBitToJamRam(TestMode &TM){
JamRamAddress=TM.getAddr();
JamRamData=TM.getValue();
apg_jam_ram_set(JamRamAddress,JamRamData);
}
//running TestModeGroupObject.store(address, data) will find which test mode that jamram address is for and set the appropriate test mode value for later printing.
//This is meant to be used in conjuction with the Excel spreadsheet method of entering test modes
void store(int address, int data){
JamRamAddress= address;
JamRamData = data;
output("Current JamRamAddress = %d JamRamData = %d", JamRamAddress, JamRamData);
apg_jam_ram_set(JamRamAddress,JamRamData);
for(std::vector<TestMode>::iterator it = TestModeVector.begin(); it!=TestModeVector.end(); ++it){
compareAndStore(*it);
}
}
//Running TestModeGroupObject.load() will commit all test mode changes to the jamram for test modes that are part of that object
void load(void){
for(std::vector<TestMode>::iterator it = TestModeVector.begin(); it!=TestModeVector.end(); ++it){
writeTmBitToJamRam(*it);
}
}
int getTMVal(TestMode &TM){
return TM.getValue();
}
private:
int JamRamAddress;
int JamRamData;
std::vector<TestMode> TestModeVector;
};
Here's how I defined the vector:
TestMode adm_testmodes[] = {TM_TWINWL,TM_TWINBL,ON_2WL,ON_2BL,WV_S1X,WV_S0X,TM_PCHG_RH_3,TM_PCHG_RH_2,TM_PCHG_RH_1,TM_PCHG_RH_0,TM_PCHG_RH_BYP,TM_PCHG_SF_3,TM_PCHG_SF_2,TM_PCHG_SF_1,TM_PCHG_SF_0,TM_PCHG_SF_BYP,
TM_PCHG_V04_3,TM_PCHG_V04_2,TM_PCHG_V04_1,TM_PCHG_V04_0,TM_PCHG_V04_BYP,TM_SA_DIS,TM_TS_NEGSLOPE,TM_TRIM_4,TM_TRIM_3,TM_TRIM_2,TM_TRIM_1,TM_TRIM_0,TM_TSSLP_2,TM_TSSLP_1,TM_TSSLP_0,
TM_WRV_N_2,TM_WRV_N_1,TM_WRV_N_0,TM_SAGAIN_EN,TM_REFTRIM_EN,TM_READ_DONE_OPT_EN,EnableCore_Read,SA_4,TM_OC_2,TM_OC_1,TM_OC_0,TM_WRLC_4,TM_WRLC_3,TM_WRLC_2,TM_WRLC_1,TM_WRLC_0,
TM_WRHC_4,TM_WRHC_3,TM_WRHC_2,TM_WRHC_1,TM_WRHC_0,TM_FTOP_3,TM_FTOP_2,TM_FTOP_1,TM_FTOP_0,TM_RISE_1,TM_RISE_0,TM_WRH_3,TM_WRH_2,TM_WRH_1,TM_WRH_0,TM_SET_4,TM_SET_3,TM_SET_2,TM_SET_1,TM_SET_0,
TM_REFSTART,TM_REFSEL_1,TM_REFSEL_0,TM_REFSL_5,TM_REFSL_4,TM_REFSL_3,TM_REFSL_2,TM_REFSL_1,TM_REFSL_0,TM_REFSH_5,TM_REFSH_4,TM_REFSH_3,TM_REFSH_2,TM_REFSH_1,TM_REFSH_0,TM_READ_DONE_ADD,
TM_READ_DONE_OPT,TM_READ_DONE_5,TM_READ_DONE_4,TM_READ_DONE_3,TM_SAGAIN_1,TM_SAGAIN_0,TM_REFR_5,TM_REFR_4,TM_REFR_3,TM_REFR_2,TM_REFR_1,TM_REFR_0
};
std::vector<TestMode> ADM_TMs (adm_testmodes, adm_testmodes + sizeof(adm_testmodes) / sizeof(TestMode));
TestModeGroup ADM_TestModeGroup(ADM_TMs);
So far so good. I can directly access all of the TestModes that are defined, change the value and have that change persist everywhere. The problem comes when I try to run the "store" function within the TestModeGroup class. It seems that I have a local copy of the TestModes that gets updated, but not the original TestMode. I'm sure that this is a pretty simple problem but I'm struggling. C++ is not my specialty, and OOP even less so.
Here's a quick example for a dummy testmode I created:
output("DUMMY_TESTMODE Initial Value: %d", DUMMY_ADM_TestModeGroup.getTMVal(DUMMY_TESTMODE));
DUMMY_TESTMODE.setValue(1);
output("DUMMY_TESTMODE Set to 1 Value: %d", DUMMY_ADM_TestModeGroup.getTMVal(DUMMY_TESTMODE));
DUMMY_TESTMODE.setValue(0);
output("DUMMY_TESTMODE Set to 0 Value: %d", DUMMY_ADM_TestModeGroup.getTMVal(DUMMY_TESTMODE));
DUMMY_ADM_TestModeGroup.store(22,1);
output("DUMMY_TESTMODE Set to 1 Value: %d", DUMMY_ADM_TestModeGroup.getTMVal(DUMMY_TESTMODE));
doing the .setValue works fine, but doing the .store does not. When I print out the value it comes back as 0. Within the .store function I do a printout, though, and that gives the right value of 1. Somehow I think I am simply altering a copy of the original vector, but I just can't figure it out. I've been driving myself crazy and nobody I've talked to knows enough about C++ to help. Does anybody have any insight as to where I've screwed up?
Try changing TestModeGroup class:
class TestModeGroup{
public:
TestModeGroup(const std::vector<TestMode> &TestModes) : TestModeVector(TestModes)
{}
// ...
std::vector<TestMode> & TestModeVector;
};
If you want vector modifications done inside class to be applied to the original value passed to the constructor, you need to store a reference to the original object. Let me know if this helped :)