gmock gtest how to setup the mock - c++

Please consider the below code sample
NodeInterface * pPreNode = NULL;
NodeInterface * pChild = NULL;
for (uint16_t Index = 0; Index < Children.size(); ++Index)
{
pChild = Children[Index];
if (pPreNode == NULL)
{
pChild->SetPrevious(pChild);
pChild->SetNext(pChild);
}
else
{
pChild->SetNext(pPreNode->GetNext());
pChild->SetPrevious(pPreNode);
pPreNode->GetNext()->SetPrevious(pChild);
pPreNode->SetNext(pChild);
}
pPreNode = pChild;
}
To test this lines how to setup the mock exactly?
Children is a vector of Nodes and we are passing Mocked objects.
EXPECT_CALL(Obj, GetNode()).WillOnce(Invoke(this, &GetANewNode));
and the GetANewNode will provide new MockedNode
MockedNode * GetANewNode()
{
MockedNode * pMockedNode = new MockedNode();
return pMockedNode;
}
How to provide exact nodes for each Next(), Previous() calls?
EXPECT_CALL(*pMockedNode, SetNext(_));
EXPECT_CALL(*pMockedNode, SetPrevious(_));
EXPECT_CALL(*pMockedNode, GetNext());
EXPECT_CALL(*pMockedNode, GetPrevious());

Simple solution is to have all mocked nodes predefined before test case. And use Sequence/InSequence to be sure that everything happens in proper order.
class ObjTest : public ::testing::Test
{
protected:
const std::size_t N = ...; // I do not know how many do you need
std::vector<MockedNode> mockedNode;
std::vector<Node*> children;
Sequence s;
.... Obj; // I am not sure what is Obj in your question
ObjTest () : mockedNode(N)
{}
void SetUp() override
{
// initial setup
EXPECT_CALL(Obj, GetNode()).WillOnce(Return(&mockedNode.front())).InSequence(s);
}
};
Having such test class with initial setup - you can create test cases testing various scenarios that happen after initial sequence:
TEST_F(ObjTest, shouldLinkOneNodeToItself)
{
std::vector<Node*> children { &mockedNode[0] };
EXPECT_CALL(mockedNode[0], SetNext(&mockedNode[0])).InSequence(s);
EXPECT_CALL(mockedNode[0], SetPrevious(&mockedNode[0])).InSequence(s);
Obj.testedFunction(children); // you have not provided your tested function name...
}
And very similar test case for two children:
TEST_F(ObjTest, shouldLinkTwoNodesToEachOther)
{
std::vector<Node*> children { &mockedNode[0], &&mockedNode[1] };
// first interation
EXPECT_CALL(mockedNode[0], SetNext(&mockedNode[0])).InSequence(s);
EXPECT_CALL(mockedNode[0], SetPrevious(&mockedNode[0])).InSequence(s);
// second iteration
EXPECT_CALL(mockedNode[0], GetNext()).WillOnce(Return(&mockedNode[0])).InSequence(s);
EXPECT_CALL(mockedNode[1], SetNext(&mockedNode[0])).InSequence(s);
EXPECT_CALL(mockedNode[1], SetPrevious(&mockedNode[0])).InSequence(s);
// etc...
Obj.testedFunction(children);
}

Related

How to iterate over a template class?

I'm trying to create a generic menu class that will be used with a 4 line LCD.
I have a specific (non template) version working, but want to extend it to allow the menu to modify a variety of data types (int, float, unsigned...).
Here's the non template version that's working as expected...
/*
* ideally this design allows for defining an arbitrary menu as shown below
* example...
* root1
* sub1-1
* sub1-2
* root 2
* root 3
* sub3-1
* sub3-2
* sub3-2-1
* sub3-2-2
*
* each node in the menu can be executed, and allow for moving to the next/prev sibling or child/parent
* this flexibility requires that each node contains pointers to parent, child, and sibling nodes.
*/
class MenuNode
{
private:
char *prompt;
int value;
public:
MenuNode *parent=NULL;
MenuNode *child=NULL;
MenuNode *prevSibling=NULL;
MenuNode *nextSibling=NULL;
void SetValue(int value)
{
this->value = value;
}
int GetValue()
{
return value;
}
char *Prompt()
{
return prompt;
}
MenuNode(char *prompt, int initialValue, MenuNode *parent, MenuNode *prevSibling)
{
Serial.print(prompt);Serial.println(F(" MenuNode"));
this->prompt = prompt;
if (prevSibling != NULL)
{
this->prevSibling = prevSibling;
prevSibling->SetNextSibling(this);
this->parent = prevSibling->parent;
}
// prevSibling if provided sets the parent
if (prevSibling==NULL && parent != NULL)
{
this->parent = parent;
this->parent->SetChild(this);
}
value = initialValue;
}
void SetChild(MenuNode *child)
{
Serial.print(prompt);Serial.println(F(" SetChild"));
this->child = child;
}
void SetNextSibling(MenuNode *nextSibling)
{
Serial.print(prompt);Serial.println(F(" SetNextSibling"));
this->nextSibling = nextSibling;
}
};
Here's some test code that creates the menu structure...
// Test menu...
MenuNode r1("R1",10,NULL,NULL);
MenuNode r2("R2",20,NULL,&r1);
MenuNode r21("R21",30,&r2,NULL);
MenuNode r22("R22",40,&r2,&r21); // setting parent is optional, the parent will be set by the prev sibling parent
MenuNode r221("R221",50,&r22,NULL);
MenuNode r2211("R2211",60,&r221,NULL);
MenuNode r2212("R2212",70,NULL,&r2211);
MenuNode r3("R3",30,NULL,&r2);
This code iterates over each element printing out the structure
void PrintMenuStructure(MenuNode *node,int offset)
{
while(node != NULL)
{
for (int i=0;i<offset;i++)
Serial.print("-");
Serial.print(node->Prompt());
Serial.print(" = ");
Serial.print(node->Value());
if (node->parent != NULL)
{
Serial.print(" parent=");
Serial.print(node->parent->Prompt());
}
if (node->prevSibling != NULL)
{
Serial.print(" prevSib=");
Serial.print(node->prevSibling->Prompt());
}
if (node->nextSibling != NULL)
{
Serial.print(" nextSib=");
Serial.print(node->nextSibling->Prompt());
}
if (node->child != NULL)
{
Serial.print(" child=");
Serial.print(node->child->Prompt());
}
Serial.println();
if (node->child != NULL)
PrintMenuStructure(node->child,++offset);
node = node->nextSibling;
}
}
This is the output of the previous function demonstrating the structure of the menu...
R1 = 10 nextSib=R2
R2 = 20 prevSib=R1 nextSib=R3 child=R21
-R21 = 30 parent=R2 nextSib=R22
-R22 = 40 parent=R2 prevSib=R21 child=R221
--R221 = 50 parent=R22 child=R2211
---R2211 = 60 parent=R221 nextSib=R2212
---R2212 = 70 parent=R221 prevSib=R2211
-R3 = 30 prevSib=R2
It all works the way I want, but GetValue/SetValue only operate on int data.
I can create a template version of the class, with the data types of GetValue and SetValue defined by the template parameter, but I don't know now to iterate over the nodes once I do that.
Seems like a simple enough task, but I've been beating my head against the wall for a while, and haven't come up with anything that works. Any help pointing me in the right direction would be appreciated.
I'm trying to figure out how to iterate over a linked list of classes, but can't figure out how to get a pointer to start iterating.
Sorry, I couldn't get the code formatting to work... :(
The way I interpret your requirement: it seems your should make your
int value;
a std::variant.
That's the lowest cost path.
If you templatize the MenuNode class with its value type. Then a MenuNode<int>* cannot be the parent of a MenuNode<float*>, etc. Not without some effort. You'd probably better off make it polymorphic by derivate each type of value your want to support from a common abstract virtual base, and depend on how you want to use the value, design your interface.

In Unreal C++ why are structs added to a TArray in an async thread not removed from RAM?

I am asking this question from an Unreal Engine C++ code point of view but I am wondering if my problem is more to do with the nuances of C++'s way of operating.
I have a Unreal actor. A simple class that holds an array of my own structs and runs a timer which triggers my own function. This function passes a reference of the actors array to an asynchronous task.
This async task then goes to work, first creating a new struct, then adding two floats to its own internal TArray of floats and then adds that struct to the main actors array.
The problem:
After the async task has completed and I delete the actor from the level editor window, the system RAM is decreased as I call the Empty() function on the main actors array in the Destroyed() function but the RAM used by all of the structs (ie: The float array inside each struct) is left in memory and never cleared out.
Observations:
If I do not use an async task and run the same function inside the main thread ALL of the memory is cleared successfully.
If I do not create the struct inside the async task and instead initalize the array with a load of structs which in turn are initialized with N number of floats inside the main thread, then pass that as a reference to the async task which works on the data, then the memory is also cleared out successfully.
What I would like to happen
I would like to pass a reference of the main actors array of structs to the async task. The async task would then go to work creating the data. Once it is complete, the main actor would then have access to the data and when the actor is deleted in the level editor window, ALL of the memory would be freed.
The code:
The definition of the data struct I am using:
struct FMyDataStruct
{
TArray<float> ArrayOfFloats;
FMyDataStruct()
{
ArrayOfFloats.Empty();
ArrayOfFloats.Shrink();
}
FMyDataStruct(int32 FloatCount)
{
ArrayOfFloats.Init(0.f, FloatCount);
}
~FMyDataStruct()
{
ArrayOfFloats.Empty();
ArrayOfFloats.Shrink();
}
};
The main actors definition of the array I am using:
TArray<FMyDataStruct> MyMainArray;
The main actors custom function I am running:
//CODE 1: This part DOES empty the RAM when run (ie: Run on main thread)
/*for (int32 Index = 0; Index < 50000000; Index++)
{
FMyDataStruct MyDataStruct;
MyDataStruct.ArrayOfFloats.Add(FMath::Rand());
MyDataStruct.ArrayOfFloats.Add(FMath::Rand());
MyMainArray.Add(MyDataStruct);
}*/
//CODE 2: This does NOT empty the RAM when run. The two floats * 50,000,000 are left in system memory after the actor is deleted.
auto Result = Async(EAsyncExecution::Thread, [&]()
{
for (int32 Index = 0; Index < 50000000; Index++)
{
FMyDataStruct MyDataStruct;
MyDataStruct.ArrayOfFloats.Add(FMath::Rand());
MyDataStruct.ArrayOfFloats.Add(FMath::Rand());
MyMainArray.Add(MyDataStruct);
}
});
An example of initializing the array in the main thread, then working on it inside the async task:
//Initialize the array and its structs (plus the float array inside the struct)
MyMainArray.Init(FMyDataStruct(2), 50000000);
//TFuture/Async task
auto Result = Async(EAsyncExecution::Thread, [Self]()
{
for (int32 Index = 0; Index < 50000000; Index++)
{
Self->MyMainArray[Index].ArrayOfFloats[0] = FMath::Rand();
Self->MyMainArray[Index].ArrayOfFloats[1] = FMath::Rand();
}
//Call the main threads task completed function
AsyncTask(ENamedThreads::GameThread, [Self]()
{
if (Self != nullptr)
{
Self->MyTaskComplete();
}
});
});
Final thoughts:
Ultimately what I am asking is can anyone explain to me why from a C++ point of view the structs and their data would be removed from memory successfully when created/added from the main thread but then not removed from memory if created inside the async task/thread?
Update #1:
Here is a minimum reproducible example:
Create a new project in either Unreal Engine 4.23, 4.24 or 4.25.
Add a new C++ actor to the project and name it "MyActor".
Edit the source with the following:
MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
struct FMyDataStruct
{
FMyDataStruct()
{
//Default Constructor
}
FMyDataStruct(const FMyDataStruct& other)
: ArrayOfFloats(other.ArrayOfFloats)
{
//Copy constructor
}
FMyDataStruct(FMyDataStruct&& other)
{
//Move constructor
if (this != &other)
{
ArrayOfFloats = MoveTemp(other.ArrayOfFloats);
}
}
FMyDataStruct& operator=(const FMyDataStruct& other)
{
//Copy assignment operator
if (this != &other) //avoid self assignment
{
ArrayOfFloats = other.ArrayOfFloats; //UE4 TArray deep copy
}
return *this;
}
FMyDataStruct& operator=(FMyDataStruct&& other)
{
//Move assignment operator
if (this != &other) //avoid self assignment
{
ArrayOfFloats = MoveTemp(other.ArrayOfFloats);
}
return *this;
}
FMyDataStruct(int32 FloatCount)
{
//Custom constructor to initialize the float array
if (FloatCount > 0)
{
ArrayOfFloats.Init(0.f, FloatCount);
}
}
~FMyDataStruct()
{
//Destructor
ArrayOfFloats.Empty();
ArrayOfFloats.Shrink();
}
public:
TArray<float> ArrayOfFloats;
};
UCLASS()
class BASICPROJECT1_API AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor();
protected:
virtual void Destroyed() override;
public:
bool IsEditorOnly() const override;
bool ShouldTickIfViewportsOnly() const override;
virtual void Tick(float DeltaTime) override;
void DoSomething();
void AsyncTaskComplete();
bool bShouldCount = true;
float TimeCounter = 0.f;
TArray<FMyDataStruct> MyMainArray;
};
MyActor.cpp
#include "MyActor.h"
AMyActor::AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
}
void AMyActor::Tick(float DeltaTime)
{
if (!HasAnyFlags(RF_ClassDefaultObject)) //Check for not CDO. We only want to run in the instance
{
if (bShouldCount)
{
TimeCounter += DeltaTime;
if (TimeCounter >= 5.f)
{
bShouldCount = false;
DoSomething();
}
}
}
}
void AMyActor::Destroyed()
{
Super::Destroyed();
MyMainArray.Empty();
MyMainArray.Shrink();
UE_LOG(LogTemp, Warning, TEXT("Actor got Destroyed!"));
}
bool AMyActor::IsEditorOnly() const
{
return true;
}
bool AMyActor::ShouldTickIfViewportsOnly() const
{
return true;
}
void AMyActor::DoSomething()
{
//Change the code that is run:
//1 = Main thread only
//2 = Async only
//3 = Init on main thread and process in async task
//======================
int32 CODE_SAMPLE = 1;
UE_LOG(LogTemp, Warning, TEXT("Actor is running DoSomething()"));
TWeakObjectPtr<AMyActor> Self = this;
if (CODE_SAMPLE == 1)
{
//CODE 1: Run on main thread. This part DOES empty the RAM when run. BLOCKS the editor window.
//=========================================================================
MyMainArray.Empty();
MyMainArray.Shrink();
MyMainArray.Reserve(50000000);
for (int32 Index = 0; Index < 50000000; Index++)
{
FMyDataStruct MyDataStruct;
MyDataStruct.ArrayOfFloats.Reserve(2);
MyDataStruct.ArrayOfFloats.Emplace(FMath::Rand());
MyDataStruct.ArrayOfFloats.Emplace(FMath::Rand());
MyMainArray.Emplace(MyDataStruct);
}
UE_LOG(LogTemp, Warning, TEXT("Main thread array fill is complete!"));
}
else if (CODE_SAMPLE == 2)
{
//CODE 2: Run on async task. This does NOT empty the RAM when run
//(4 bytes per float * 2 floats * 50,000,000 structs = 400Mb is left in system memory after the actor is deleted)
//=========================================================================
auto Result = Async(EAsyncExecution::Thread, [Self]()
{
if (Self != nullptr)
{
Self->MyMainArray.Empty();
Self->MyMainArray.Shrink();
Self->MyMainArray.Reserve(50000000);
for (int32 Index = 0; Index < 50000000; Index++)
{
FMyDataStruct MyDataStruct;
MyDataStruct.ArrayOfFloats.Reserve(2);
MyDataStruct.ArrayOfFloats.Emplace(FMath::Rand());
MyDataStruct.ArrayOfFloats.Emplace(FMath::Rand());
Self->MyMainArray.Emplace(MyDataStruct);
}
AsyncTask(ENamedThreads::GameThread, [Self]()
{
if (Self != nullptr)
{
Self->AsyncTaskComplete();
}
});
}
});
}
else if (CODE_SAMPLE == 3)
{
//CODE 3: Initialize the array in the main thread and work on the data in the async task
//=========================================================================
MyMainArray.Init(FMyDataStruct(2), 50000000);
auto Result = Async(EAsyncExecution::Thread, [Self]()
{
if (Self != nullptr)
{
for (int32 Index = 0; Index < 50000000; Index++)
{
Self->MyMainArray[Index].ArrayOfFloats[0] = FMath::Rand();
Self->MyMainArray[Index].ArrayOfFloats[1] = FMath::Rand();
}
AsyncTask(ENamedThreads::GameThread, [Self]()
{
if (Self != nullptr)
{
Self->AsyncTaskComplete();
}
});
}
});
}
}
void AMyActor::AsyncTaskComplete()
{
UE_LOG(LogTemp, Warning, TEXT("Async task is complete!"));
}
Compile and run the project.
Drag the actor into the level editor window.
After 5 seconds the code will run and the RAM usage will increase to 1750Mb.
Select the actor in the outliner window and delete it.
The RAM usage will perform like this:
CODE 1: RAM is cleared out all the way to the starting RAM usage of 650Mb.
CODE 2: RAM is cleared down to 1000Mb and never returns to starting usage.
CODE 3: RAM is cleared out all the way to the starting RAM usage of 650Mb.
I thank you for your help.

GOF Composite Design Pattern CompositeObject::Remove Recursive Implementation in C++

This is the part of question from my question asked in codereview website:
GOF Composite Design Pattern Implementation Using Modern C++
The post has complete information/implementation about it but here I am posting this question to understand about the following information:
How to implement CompositeEquipment::Remove?.
Based on my understanding, it should do recursive search in all composite object in which client has invoked and recursively all its child objects which can also be of composite type. Just to illustrate from above implementation, if client write the as cabinet->Remove(bus); it would not remove bus object as it is the child of chassis object. This seems to be incorrect to me. However I am not able to implement the CompositeEquipment::Remove in such a way that it searches recursively if child objects themselves are of composite.
So far I have came of with the following implementation which just searches the composite objects which client has involved for Remove method.
//To find out whether items are in the composite objects
class Name_Equal {
private:
Equipment::EquipmentSmartPtr val;
public:
Name_Equal(const Equipment::EquipmentSmartPtr& v) :val(v) { }
bool operator()(const Equipment::EquipmentSmartPtr& x) const {
return (x->Name() == val->Name());
}
};
void CompositeEquipment::Remove(EquipmentSmartPtr entry) {
find_equipment(_equipment, entry);
}
void CompositeEquipment::find_equipment(std::vector<EquipmentSmartPtr>& vec,
EquipmentSmartPtr& entry){
Name_Equal eq(entry);
auto itrpos = std::find_if(std::begin(vec), std::end(vec), eq);
if (itrpos != std::end(vec)) {
vec.erase(itrpos);
}
}
Kindly let me know in case any additional information or complete code needs to post here as well.
There are two options:
Provide a virtual function Remove in the base class and make it a noop implementation. Then add a few more lines to CompositeEquipment::find_equipment.
void CompositeEquipment::find_equipment(std::vector<EquipmentSmartPtr>& vec,
EquipmentSmartPtr& entry){
Name_Equal eq(entry);
auto itrpos = std::find_if(std::begin(vec), std::end(vec), eq);
if (itrpos != std::end(vec)) {
vec.erase(itrpos);
} else {
for ( EquipmentSmartPtr sptr : vec )
{
sptr->Remove(entry);
}
}
}
Use dynamic_cast to determine whether an item of the composite is a composite also. If so, call Remove on it. I prefer this option.
void CompositeEquipment::find_equipment(std::vector<EquipmentSmartPtr>& vec,
EquipmentSmartPtr& entry){
Name_Equal eq(entry);
auto itrpos = std::find_if(std::begin(vec), std::end(vec), eq);
if (itrpos != std::end(vec)) {
vec.erase(itrpos);
} else {
for ( EquipmentSmartPtr sptr : vec )
{
Equipment* ptr = dynamic_cast<Equipment*>(sptr.get());
if ( ptr )
{
ptr->Remove(entry);
}
}
}
}
A bit about names... find_equipment seems a strange name for the function. I would put the whole thing in Remove.
void CompositeEquipment::Remove(EquipmentSmartPtr& entry){
std::vector<EquipmentSmartPtr>& vec = _equipment;
Name_Equal eq(entry);
auto itrpos = std::find_if(std::begin(vec), std::end(vec), eq);
if (itrpos != std::end(vec)) {
vec.erase(itrpos);
} else {
for ( EquipmentSmartPtr sptr : vec )
{
Equipment* ptr = dynamic_cast<Equipment*>(sptr.get());
if ( ptr )
{
ptr->Remove(entry);
}
}
}
}

Generic Interfaces for ICustomerRetList and ICustomerRet - QBFC

Any QBFC developers out there? I'm using QBFC to pull multiple different types of objects out of Quickbooks: Customers, Items, Invoices, TaxCodes, etc. The data query code really only varies once you get to the Ret object so I'm trying to build some functions to abstract the process.
A typical repose object looks like
IReponseList
IResponse
RetList
Ret
IResponseList and IResponse are both generic enough to work on all query response types. However, there doesn't appear to be a generic RetList and Ret Interface that I can use for the abstraction. I only have type-sepecific interfaces like ICustomerRetList, ISalesTaxCodeRetList, etc. I'd like to write the code independent of what TYPE of return list it is....
Is there an interface for RetList or Ret that I just can't seem to find?
Thanks
The interface IQBBase is the closest thing to what you're looking for. Most everything in QBFC is derived from IQBase, including all query types and all return types. Using IQBBase references and .NET generics it is possible to create a framework to deal with query results.
Update: the iterator example below is now available as part of the Zombie library for QBFC, which you can grab from github.
For example, here's a generic iterator that takes the RetList type and Ret type as parameters:
/// <summary>
/// This generic class simplifies and standardizes iteration syntax
/// for QBFC lists. Using this class we can use the foreach keyword
/// to iterate across all items in a list.
/// </summary>
/// <typeparam name="L">The type of the list, for example IBillRetList</typeparam>
/// <typeparam name="D">The type of the item, for example IBillRet</typeparam>
public class QBFCIterator<L, D>:IEnumerable<D> where L : class, IQBBase
{
private L m_List;
/// <summary>
/// This constructor can be used for response list items or for sub-lists that are properties
/// on other QBFC objects.
/// </summary>
/// <param name="lst">The sub-list</param>
public QBFCIterator(IQBBase lst)
{
m_List = lst as L;
if (m_List == null && lst != null)
{
throw new Exception("iterator type mismatch");
}
}
public bool IsEmpty
{
get
{
if (m_List == null)
{
return true;
}
else
{
return Count == 0;
}
}
}
/// <summary>
/// An efficient alternative to the Count() function
/// </summary>
public int EntityCount
{
get { return Count; }
}
public D GetFirstItem()
{
if (IsEmpty)
{
throw new Exception("Cannot retrieve item from empty list");
}
else
{
return GetAt(0);
}
}
#region Late-bound properties
//
// Since .NET requires that all methods invoked on a parameterized type
// must compile based solely on interface constraints, we must use late
// binding to access the count property and GetAt methods. This may have
// an impact on performance and could conceivably cause run time errors
// with incorrect type parameters.
//
private int Count
{
get
{
if (m_List == null)
{
return 0;
}
else
{
Type t = m_List.GetType();
return (int)t.InvokeMember("Count",
System.Reflection.BindingFlags.GetProperty, null, m_List, null);
}
}
}
private D GetAt(int idx)
{
Type t = m_List.GetType();
return (D)t.InvokeMember("GetAt",
System.Reflection.BindingFlags.InvokeMethod, null, m_List, new Object[] { idx });
}
#endregion
#region IEnumerable<D> Members
public IEnumerator<D> GetEnumerator()
{
if (m_List != null)
{
for (int idx = 0; idx < Count; idx++)
{
yield return GetAt(idx);
}
}
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
if (m_List != null)
{
for (int idx = 0; idx < Count; idx++)
{
yield return GetAt(idx);
}
}
}
#endregion
}

Inherited variables are not reading correctly when using bitwise comparisons

I have a few classes set up for a game, with XMapObject as the base, and XEntity, XEnviron, and XItem inheriting it.
MapObjects have a number of flags, one of them being MAPOBJECT_SOLID. My problem is that XEntity is the only class that correctly detects MAPOBJECT_SOLID. Both Items are Environs are always considered solid by the game, regardless of the flag's state. What is important is that Environs and Item should almost never be solid.
Each class has a very basic preliminary constructor, just initializing all varibles to zero or NULL. During the CreateX() phase, Objects are linked into the map, set into a linked linked list.
Both XItem and XEnviron are a tad sloppy. They are both new, and in the middle or my debugging attempts.
Here are the relevent code samples:
XMapObject:
#define MAPOBJECT_ACTIVE 1
#define MAPOBJECT_RENDER 2
#define MAPOBJECT_SOLID 4
class XMapObject : public XObject
{
public:
Uint8 MapObjectType,Location[2],MapObjectFlags;
XMapObject *NextMapObject,*PrevMapObject;
XMapObject();
void CreateMapObject(Uint8 MapObjectType);
void SpawnMapObject(Uint8 MapObjectLocation[2]);
void RemoveMapObject();
void DeleteMapObject();
void MapObjectSetLocation(Uint8 Y,Uint8 X);
void MapObjectMapLink();
void MapObjectMapUnlink();
};
XMapObject::XMapObject()
{
MapObjectType = 0;
Location[0] = 0;
Location[1] = 1;
NextMapObject = NULL;
PrevMapObject = NULL;
}
void XMapObject::CreateMapObject(Uint8 Type)
{
MapObjectType = Type;
}
void XMapObject::SpawnMapObject(Uint8 MapObjectLocation[2])
{
if(!(MapObjectFlags & MAPOBJECT_ACTIVE)) { MapObjectFlags += MAPOBJECT_ACTIVE; }
Location[0] = MapObjectLocation[0];
Location[1] = MapObjectLocation[1];
MapObjectMapLink();
}
XEntity:
XEntity *StartEntity = NULL,*EndEntity = NULL;
class XEntity : public XMapObject
{
public:
Uint8 Health,EntityFlags;
float Speed,Time;
XEntity *NextEntity,*PrevEntity;
XItem *IventoryList;
XEntity();
void CreateEntity(Uint8 EntityType,Uint8 EntityLocation[2]);
void DeleteEntity();
void EntityLink();
void EntityUnlink();
Uint8 MoveEntity(Uint8 YOffset,Uint8 XOffset);
};
XEntity::XEntity()
{
Health = 0;
Speed = 0;
Time = 1.0;
EntityFlags = 0;
NextEntity = NULL;
PrevEntity = NULL;
IventoryList = NULL;
}
void XEntity::CreateEntity(Uint8 EntityType,Uint8 EntityLocation[2])
{
CreateMapObject(EntityType);
SpawnMapObject(EntityLocation);
if(!(MapObjectFlags & MAPOBJECT_SOLID) { MapObjectFlags += MAPOBJECT_SOLID; }
EntityFlags = ENTITY_CLIPPING;
Time = 1.0;
Speed = 1.0;
EntityLink();
}
void XEntity::EntityLink()
{
if(StartEntity == NULL)
{
StartEntity = this;
PrevEntity = NULL;
NextEntity = NULL;
}
else
{
EndEntity->NextEntity = this;
}
EndEntity = this;
}
XEnviron:
class XEnviron : public XMapObject
{
public:
Uint8 Effect,TimeOut;
void CreateEnviron(Uint8 Type,Uint8 Y,Uint8 X,Uint8 TimeOut);
};
void XEnviron::CreateEnviron(Uint8 EnvironType,Uint8 Y,Uint8 X,Uint8 TimeOut)
{
CreateMapObject(EnvironType);
Location[0] = Y;
Location[1] = X;
SpawnMapObject(Location);
XTile *Tile = GetTile(Y,X);
Tile->Environ = this;
MapObjectFlags = MAPOBJECT_ACTIVE + MAPOBJECT_SOLID;
printf("%i\n",MapObjectFlags);
}
XItem:
class XItem : public XMapObject
{
public:
void CreateItem(Uint8 Type,Uint8 Y,Uint8 X);
};
void XItem::CreateItem(Uint8 Type,Uint8 Y,Uint8 X)
{
CreateMapObject(Type);
Location[0] = Y;
Location[1] = X;
SpawnMapObject(Location);
}
And lastly, the entity move code. Only entities are capable of moving themselves.
Uint8 XEntity::MoveEntity(Uint8 YOffset,Uint8 XOffset)
{
Uint8
NewY = Location[0] + YOffset,
NewX = Location[1] + XOffset;
if((NewY >= 0 && NewY < MAPY) && (NewX >= 0 && NewX < MAPX))
{
XTile *Tile = GetTile(NewY,NewX);
if(Tile->MapList != NULL)
{
XMapObject *MapObject = Tile->MapList;
while(MapObject != NULL)
{
if(MapObject->MapObjectFlags & MAPOBJECT_SOLID)
{
printf("solid\n");
return 0;
}
MapObject = MapObject->NextMapObject;
}
}
if(Tile->Flags & TILE_SOLID && EntityFlags & ENTITY_CLIPPING)
{
return 0;
}
this->MapObjectSetLocation(NewY,NewX);
return 1;
}
return 0;
}
What is wierd, is that the bitwise operator always returns true when the MapObject is an Environ or an Item, but it works correctly for Entities. For debug I am using the printf "Solid", and also a printf containing the value of the flag for both Environs and Items.
Any help is greatly appreciated, as this is a major bug for the small game I am working on. I am also very new at Object Oriented programming, anything tips, suggestions and/or criticism are also welcome.
Your problem appears to be that you never initialize MapObjectFlags in any classes other than XEnviron so, as a basic type, it will have an unspecified value in XItem, XEntity and other XMapObject derived objects. I suggest that, as a member of XMapObject you explicitly initialize it to a known value.
As a rule, it is generally a good idea to ensure that all members of basic type are explicitly initialized in the initializer list of every constructor that you define.
e.g.
XMapObject()
: MapObjectFlags(0)
, // ... other initializers
{
// Other initializations
}
You can't (legally) be calling XEntity::MoveEntity on a MapObject or Environ because they don't have such a method. If you're using static_cast to change your object pointer into an XEntity so you can call MoveEntity on it, then you really have no guarantees about how the bit operation will work. In some implementations, things may appear to work in MoveEntity, but what's actually happening is it's interpreting the other object's memory as an XEntity. When it tries to access the offset where it believes MapObjectFlags exists, it's not actually there and always has that bit set to 1.
I figured out the problem earlier today - It didn't have any relation to OO programming, inheritance, or bitwise; it was a simple scope error.
The problem was in the fact that during my quick test to get an Environ in game, I declared the new variable inside of the control switch sequence, so the next time any control was used, the Environ would act in unpredictable ways.
switch(Event.key.keysym.sym)
{
...
case SDLK_c: { XEnviron Environ; Environ.InitEnviron(...); }
...
}