I'm using tinyxml to parse xml files, and I've found that error handling here lends itself to arrow code. Our error handling is simply reporting a message to a file.
Here is an example:
const TiXmlElement *objectType = dataRoot->FirstChildElement( "game_object" );
if ( objectType ) {
do {
const char *path = objectType->Attribute( "path" );
if ( path ) {
const TiXmlElement *instance = objectType->FirstChildElement( "instance" );
if ( instance ) {
do {
int x, y = 0;
instance->QueryIntAttribute( "x", &x );
instance->QueryIntAttribute( "y", &y );
if ( x >= 0 && y >= 0 ) {
AddGameObject( new GameObject( path, x, y ));
} else {
LogErr( "Tile location negative for GameObject in state file." );
return false;
}
} while ( instance = instance->NextSiblingElement( "instance" ));
} else {
LogErr( "No instances specified for GameObject in state file." );
return false;
}
} else {
LogErr( "No path specified for GameObject in state file." );
return false;
}
} while ( objectType = objectType->NextSiblingElement( "game_object" ));
} else {
LogErr( "No game_object specified in <game_objects>. Thus, not necessary." );
return false;
}
return true;
I'm not huffing and puffing over it, but if anyone can think of a cleaner way to accomplish this it would be appreciated.
P.S. Exceptions not an option.
Edit:
Would something like this be preferable?
if ( !path ) {
// Handle error, return false
}
// Continue
This eliminates the arrow code, but the arrow code kind of puts all of the error logging on one place.
Using return values as error codes just leads to such code, it can't be improved much. A slightly cleaner way would use goto to group all error handling into a single block and to decrease the nesting of blocks.
This does however not solve the actual problem, which is using return values as error codes. In C, there is no alternative, but in C++ exceptions are available and should be used. If they are not an option, you're are stuck with what you have.
You could create a macro for that, which encapsulates the if (!var) { .. return false; } and error reporting.
However, I do not see how this can be improved all that much; its just the way it is. C'est la vie. C'est le code...
I'm not huffing and puffing over it,
but if anyone can think of a cleaner
way to accomplish this it would be
appreciated.
I have replaced the nested ifs with return statements on error (this makes the code "flow down" instead of going "arrow shaped". I have also replaced your do loopps with for loops (so I could understand it better).
Is this what you wanted?
const TiXmlElement *objectType = dataRoot->FirstChildElement( "game_object" );
if ( !objectType ) {
LogErr( "No game_object specified in <game_objects>. Thus, not necessary." );
return false;
}
for(; objectType != 0; objectType = objectType->NextSiblingElement( "game_object" )) {
const char *path = objectType->Attribute( "path" );
if ( !path ) {
LogErr( "No path specified for GameObject in state file." );
return false;
}
const TiXmlElement *instance = objectType->FirstChildElement( "instance" );
if ( !instance ) {
LogErr( "No instances specified for GameObject in state file." );
return false;
}
for(; instance != 0; instance = instance->NextSiblingElement( "instance" )) {
int x, y = 0;
instance->QueryIntAttribute( "x", &x );
instance->QueryIntAttribute( "y", &y );
if ( x >= 0 && y >= 0 ) {
AddGameObject( new GameObject( path, x, y ));
} else {
LogErr( "Tile location negative for GameObject in state file." );
return false;
}
}
}
return true;
I know it is a little late, but I know that QueryIntAttribute returns a value which can be used for error handling in case you want this for your attributes too.
if (instance->QueryIntAttribute("x",&x)!=TIXML_SUCCESS)
cout << "No x value found";
Related
I've been following Kamran Bigdely-Shamloo's article on how to get positional information from the HTC Vive and it has worked well so far. My next step was to "listen" to button presses. I've read the documentation and it says here that all I need to do is query IVRSystem::GetControllerStateand it'll return a
"struct with the current state of the controller"
This struct however always contains variables that have the 0 value. The following function is called in a while (true) loop from the main function.
bool CMainApplication::HandleInput()
{
SDL_Event sdlEvent;
bool bRet = false;
while ( SDL_PollEvent( &sdlEvent ) != 0 )
{
if ( sdlEvent.type == SDL_QUIT )
{
bRet = true;
}
else if ( sdlEvent.type == SDL_KEYDOWN )
{
if ( sdlEvent.key.keysym.sym == SDLK_ESCAPE
|| sdlEvent.key.keysym.sym == SDLK_q )
{
bRet = true;
}
if( sdlEvent.key.keysym.sym == SDLK_c )
{
m_bShowCubes = !m_bShowCubes;
}
}
}
// Process SteamVR events
// Periodically returns an event of type 404 ("VREvent_SceneApplicationChanged = 404, // data is process - The App actually drawing the scene changed (usually to or from the compositor)"
vr::VREvent_t event;
vr::VREvent_Controller_t controllerEvent;
std::chrono::milliseconds ms4;
while( m_pHMD->PollNextEvent( &event, sizeof( event ) ) )
{
ms4 = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()
);
ProcessVREvent( &event);
printPositionalData(&event, ms4);
}
vr::VRControllerState_t state;
// Check every device attached, usually it's four devices
// Second if statement is never reached
for (int i = 0; i < 1000; i++) {
if (m_pHMD->GetControllerState(i, &state, sizeof(state))) {
dprintf("%d", i);
if (state.ulButtonPressed != 0 || state.unPacketNum != 0 || state.ulButtonTouched != 0) {
dprintf("Some action?");
}
}
}
dprintf("\n");
// Process SteamVR action state
// UpdateActionState is called each frame to update the state of the actions themselves. The application
// controls which action sets are active with the provided array of VRActiveActionSet_t structs.
vr::VRActiveActionSet_t actionSet = { 0 };
actionSet.ulActionSet = m_actionsetDemo;
vr::VRInput()->UpdateActionState( &actionSet, sizeof(actionSet), 1 );
m_bShowCubes = !GetDigitalActionState( m_actionHideCubes );
vr::VRInputValueHandle_t ulHapticDevice;
if ( GetDigitalActionRisingEdge( m_actionTriggerHaptic, &ulHapticDevice ) )
{
if ( ulHapticDevice == m_rHand[Left].m_source )
{
vr::VRInput()->TriggerHapticVibrationAction( m_rHand[Left].m_actionHaptic, 0, 1, 4.f, 1.0f, vr::k_ulInvalidInputValueHandle );
}
if ( ulHapticDevice == m_rHand[Right].m_source )
{
vr::VRInput()->TriggerHapticVibrationAction( m_rHand[Right].m_actionHaptic, 0, 1, 4.f, 1.0f, vr::k_ulInvalidInputValueHandle );
}
}
vr::InputAnalogActionData_t analogData;
if ( vr::VRInput()->GetAnalogActionData( m_actionAnalongInput, &analogData, sizeof( analogData ), vr::k_ulInvalidInputValueHandle ) == vr::VRInputError_None && analogData.bActive )
{
m_vAnalogValue[0] = analogData.x;
m_vAnalogValue[1] = analogData.y;
}
m_rHand[Left].m_bShowController = true;
m_rHand[Right].m_bShowController = true;
vr::VRInputValueHandle_t ulHideDevice;
if ( GetDigitalActionState( m_actionHideThisController, &ulHideDevice ) )
{
if ( ulHideDevice == m_rHand[Left].m_source )
{
m_rHand[Left].m_bShowController = false;
}
if ( ulHideDevice == m_rHand[Right].m_source )
{
m_rHand[Right].m_bShowController = false;
}
}
for ( EHand eHand = Left; eHand <= Right; ((int&)eHand)++ )
{
vr::InputPoseActionData_t poseData;
if ( vr::VRInput()->GetPoseActionData( m_rHand[eHand].m_actionPose, vr::TrackingUniverseStanding, 0, &poseData, sizeof( poseData ), vr::k_ulInvalidInputValueHandle ) != vr::VRInputError_None
|| !poseData.bActive || !poseData.pose.bPoseIsValid )
{
m_rHand[eHand].m_bShowController = false;
}
else
{
m_rHand[eHand].m_rmat4Pose = ConvertSteamVRMatrixToMatrix4( poseData.pose.mDeviceToAbsoluteTracking );
vr::InputOriginInfo_t originInfo;
if ( vr::VRInput()->GetOriginTrackedDeviceInfo( poseData.activeOrigin, &originInfo, sizeof( originInfo ) ) == vr::VRInputError_None
&& originInfo.trackedDeviceIndex != vr::k_unTrackedDeviceIndexInvalid )
{
std::string sRenderModelName = GetTrackedDeviceString( originInfo.trackedDeviceIndex, vr::Prop_RenderModelName_String );
if ( sRenderModelName != m_rHand[eHand].m_sRenderModelName )
{
m_rHand[eHand].m_pRenderModel = FindOrLoadRenderModel( sRenderModelName.c_str() );
m_rHand[eHand].m_sRenderModelName = sRenderModelName;
}
}
}
}
return bRet;
m_pHMD is initalized as follows:
vr::IVRSystem *m_pHMD;
....
m_pHMD = vr::VR_Init( &eError, vr::VRApplication_Background );
I must be doing something wrong because in the following snippet, the if statement is passed only for the first four iterations, which should be a controller, the vive tracker, the headset and the lighthouses. This tells me that I am able to access these states, but I am somehow not able to read the information.
for (int i = 0; i < 1000; i++) {
if (m_pHMD->GetControllerState(i, &state, sizeof(state))) {
dprintf("%d", i);
if (state.ulButtonPressed != 0 || state.unPacketNum != 0 || state.ulButtonTouched != 0) {
dprintf("Some action?");
}
}
I can't imagine its a bug, so my guess is that my configuration is faulty, or I'm doing the wrong query.
Any help is greatly appreciated!
Apparently I was making two mistakes.
Mistake #1 was that I was using the wrong sample file. I used hellovr_opengl from the OpenVr sample folder, but instead hellovr_dx12 was the working solution. It must have a different kind of configration as well, since I copied a function which was working into the hellovr_opengl project and there, it did not work! Then I copied the functions I added to the hellovr_dx12 project and was able to get the controller states of the Vive controller.
However, I wanted to get the states of the Vive Tracker, which didn't work. After some googling I found out about an issue with the current SteamVR driver, so I reverted it back to a beta hoftix, which solved the Vive Tracker issue for me.
You have to call;
vr::VRInput()->SetActionManifestPath
I have the following piece of code. It aims at traversing the [attached screenshot] Tree structure in depth first manner.
As you can see, I am interested in the highlighted entry in green -> Has Class( DatasetType ) node of this tree structure. This Tree structure is subjected to additions of new nodes by customers. So, I have to traverse the Tree structure to find my node of interest. I have formulated the below code. But And I can see that it identifies that node of my interest.
However, it is not stopping there. It is proceeding onto the next sibling i.e., Has Class( EPMJob ). I want my processing to stop. I am pretty sure that my way of returning stuff is missing something. but not able to pin point.
Any inputs are most welcome.
tag_t findHasTypeDatasetNodeInAMTree( tag_t amTreeNode )
{
CharPointer nodeName;
Response stat = askRuleName( amTreeNode, &nodeName );
CharPointer nodeArgument;
stat = askRuleArg( amTreeNode, &nodeArgument );
if( tc_strcmp( nodeName.getString(), "Has Class" ) == 0 && tc_strcmp( nodeArgument.getString(), "DatasetType" ) == 0 )
{
return amTreeNode;
}
int numChildNodes = 0;
TagPointer childNodes;
stat = askChildren( amTreeNode, &numChildNodes, &childNodes );
if( numChildNodes == 0 )
{
return NULLTAG;
}
// The following is the piece that needs attention.
// Do not want to NULL check here though
for( int inx = 0; inx < numChildNodes; ++inx )
{
findHasTypeDatasetNodeInAMTree( childNodes[inx] );
}
return NULLTAG;
}
I'm not sure what this is doing:
for( int inx = 0; inx < numChildNodes; ++inx )
{
findHasTypeDatasetNodeInAMTree( childNodes[inx] );
}
But I'm pretty sure it doesn't stop when you find something so the result is ALWAYS NULLTAG. How about something like:
for( int inx = 0; inx < numChildNodes; ++inx )
{
auto result = findHasTypeDatasetNodeInAMTree( childNodes[inx] );
if( result != NULLTAG )
return result;
}
We are getting many instances of: "CA2202 Do not dispose objects multiple times" in managed c++ with code analysis on.
To me it seems like a mistake in the code analysis, but I may be missing something.
CA2202 Do not dispose objects multiple times Object 'gcnew ConfigurationDataAssembler()' can be disposed more than once in method 'DataAssembler::CreateConfiguration(Guid, int^, int^, ObjectReference^, ObjectReference^, ObjectReference^, List^%, PLResult^%)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 935, 938 PL dataassembler.cpp 935
The two lines it mentions are "return nullptr" and "return configDTO"
I have marked those lines with comments // here, // and here
Here is the function
//---------------------------------------------------------------------------------------------------------
// For IDataAssembler
Ivara::PL::Data::UIData::Control::MCLBConfig^ DataAssembler::CreateConfiguration( System::Guid activityKey, int subscriptionID, int controlID, ObjectReference^ pRootObjRef, ObjectReference^ pSelectedObjRef, ObjectReference^ pOwningObjRef, [Out] List<Ivara::PL::Data::UIData::Control::ConfigurationListItem^>^% configList, [Out] PLResult^% result )
{
try
{
AutoStopWatch stopwatch( __FUNCTION__, LogCategories::RemotingTimings );
ThreadToActivity cTTA( activityKey );
result = PLResult::Success;
//param check
if ( subscriptionID <= 0 )
{
throw gcnew Ivara::PL::Exceptions::IvaraArgumentException( _T( "Invalid configurationID" ), _T( "configurationID" ) );
}
//fetch config
UserConfigurationOR orUserConfig( subscriptionID );
if ( !orUserConfig.isSet() )
{
result = gcnew PLResult( PLResult::eStatus::RelatedObjectNotFound, String::Format( _T( "The user configuration {0} could not be found" ), subscriptionID ) );
return nullptr;
}
UserConfiguration* pUserConfig = orUserConfig.qryObjPtr();
if ( pUserConfig == NULL )
{
result = gcnew PLResult( PLResult::eStatus::RelatedObjectNotFound, String::Format( _T( "The user configuration {0} could not be fetched, even though isSet returns true" ), subscriptionID ) );
return nullptr;
}
//create assembler
ConfigurationDataAssembler assembler;
assembler.Initialize( controlID, pRootObjRef, pSelectedObjRef, pOwningObjRef, result );
if ( result != PLResult::Success )
{
return nullptr; // here
}
Ivara::PL::Data::UIData::Control::MCLBConfig^ configDTO = assembler.AssembleConfigurationDTO( pUserConfig, configList /*out param*/, nullptr );
return configDTO; // and here
}
catch ( OTBaseException& unmanagedException )
{
throw FatalExceptionPolicy::HandleUnmanagedException( &unmanagedException, __FUNCDNAME__, __FILE__, __LINE__ );
}
catch ( Exception^ managedException )
{
throw FatalExceptionPolicy::HandleManagedException( managedException, __FUNCDNAME__, __FILE__, __LINE__ );
}
}
I have the following code, when I run the code below I get 'R6025 run-time error in Visual C++'
CommandParameterAndValue param( "Key", "value" );
parameters.AddParameter( ¶m );
parameters.HasParameter( "akeyval" );
I am lost, any ideas? Is it something to do with the casting?
typedef std::vector<iCommandParameter *> ParamsVectorList;
class CommandParametersList
{
public:
.... functions here ....
void AddParameter( iCommandParameter *param );
bool HasParameter( std::string parameterKey );
protected:
ParamsVectorList m_parameters;
};
void CommandParametersList::AddParameter( iCommandParameter *param )
{
m_parameters.push_back( param );
}
bool CommandParametersList::HasParameter( std::string parameterKey )
{
ParamsVectorList::iterator it;
CommandParameterAndValue *paramItem = NULL;
bool returnValue = false;
for ( it = m_parameters.begin(); it != m_parameters.end(); it++ )
{
paramItem = static_cast<CommandParameterAndValue *>( *it );
if ( paramItem->GetKey().compare( parameterKey ) == 0 )
{
returnValue = true;
break;
}
}
return returnValue;
}
I need more information to give a complete answer, but if you look here: http://support.microsoft.com/kb/125749
That run-time error means you tried to call a pure virtual function - it couldn't find an implementation. I would suggest running through a debugger and finding which line of code throws this error. Than it should be easy to understand and fix. It's probably happening here:
if ( paramItem->GetKey().compare( parameterKey ) == 0 )
In my NPAPI plugin, some of the objects have an "onEvent" property that is readable and writeable, and which is called on certain events.
What I have in my Javascript code will look like this:
myObject.onEvent = function( event ) {
console.log("Event: " + event );
}
// if I put this next line, the next call to the 'onEvent' handler will SIGBUS
// when there's no RetainObject() in the getter.
console.log("Event handler : " + myObject.onEvent);
And on the C++ side of the plugin, I have that kind of code:
bool MyPluginObject::getOnEvent(NPIdentifier id, NPVariant *result)
{
if( _onEvent )
{
OBJECT_TO_NPVARIANT( _onEvent, *result);
NPN_RetainObject( _onEvent ); // needed ???? why??
}
else
VOID_TO_NPVARIANT(*result);
return true;
}
bool MyPluginObject::setOnEvent( NPIdentifier id, const NPVariant *value )
{
if ( value && NPVARIANT_IS_OBJECT( *value ) )
{
if( _onEvent != NULL )
{
// release any previous function retained
NPN_ReleaseObject( _onEvent );
}
_onEvent = NPVARIANT_TO_OBJECT( *value );
NPN_RetainObject( _onEvent ); // normal retain
return true;
}
return false;
}
void MyPluginObject::onEvent(void)
{
NPVariant event = [...];
if ( _onEvent!= NULL )
{
NPVariant retVal;
bool success = NPN_InvokeDefault( _Npp, _onEvent, &event, 1, &retVal );
if( success )
{
NPN_ReleaseVariantValue(&retVal);
}
}
}
What's strange is that I've been struggling with a SIGBUS problem for a while, and once I added the NPN_RetainObject() in the getter, as you can see above, everything went fine.
I didn't find in the statement that it is needed in the Mozilla doc, neither in Taxilian's awesome doc about NPAPI.
I don't get it: when the browser requests a property that I've retained, why do I have to retain it a second time?
Should I maybe retain the function when calling InvokeDefault() on it instead? But then, why?? I already stated that I wanted to retain it.
Does getProperty() or InvokeDefault() actually does an NPN_ReleaseObject() without telling me?
You always have to retain object out-parameters with NPAPI, this is not specific to property getters.
In your specific case the object may stay alive anyway, but not in the general case:
Consider returning an object to the caller that you don't plan on keeping alive from your plugin. You have to transfer ownership to the caller and you can't return objects with a retain count of 0.