Tispe

Members
  • Content count

    760
  • Joined

  • Last visited

Community Reputation

1468 Excellent

About Tispe

  • Rank
    Advanced Member

Personal Information

  • Interests
    Programming
  1. The example I gave is just a simplification to get to the issue, ambiguous access from multiple inheritance. The component here is actually going to hold (or for that matter be) a proxy pointer to the data allocated somewhere else (in the system<T>). Should the system move the data to another memory location this component will have its proxy pointer updated by the system. The proxy pointer is built from bi-directional smart pointers to ensure that a component get/set is only one indirection away, and should either the component be destructed or the data no longer be available in the system get/set and update proxy pointer will not crash the program. With this I can create an entity and select its components using templates. I can also give it overloaded operators, such as, myEntity = pos1; and, myEntity = vel2;
  2. Hi I want to test out a polymorphic entity component system where the idea is that the components of an entity are "compositioned" using templated multiple inheritance. But I am running into an issue because I am stacking a bunch of methods with the same names inside a class (but they have different signatures). I want these methods to be overloaded by the template type but my compiler says the access is ambiguous. I have issues making them unambiguous with the using declaration because the paramter pack expansion causes a syntax error. Can anyone here give me some advice on this? template <class T> class component { T m_data; protected: component() {}; ~component() {}; public: void set(const T& data) { m_data = data; }; }; template <class ...Ts> class entity : public component<Ts>... { public: entity() {}; ~entity() {}; //using component<Ts>::set...; // syntax error }; struct position { float x{}; float y{}; float z{}; }; struct velocity { float x{}; float y{}; float z{}; }; int main() { entity<position, velocity> myEntity; position pos = { 1.0f, 1.0f, 1.0f }; velocity vel = { 2.0f, 2.0f, 2.0f }; myEntity.set(pos); // error C2385: ambiguous access of 'set' //myEntity.set(vel); return 0; }
  3.   I can see that it is slow doing random array access on entities using indices to update data in response to unpredictable input. But there is only one way to layout data in memory at one time without duplication, and it seems reasonable to me that the data layout should best suit the heaviest workload for the most gains in terms performance(fps).   I thought that when doing heavy CPU calculations such as physics/collision, it is preferable to streamline dataflow from memory to cpu cores/threads. Meaning we want to structure and pack data only relevant for those operations as closely and as contiguous as possible, am I missing something?
  4. The unique ID is supposed to be global for all entities, not just players. Over a network in client-server-client situations IDs would be consistent.   If I reserve the ID #42 for "m_Players[42]" then what would then happen if I want to to something with "m_SomeObjects[42]", for example: m_Players[13]->shoot(42,9000)? Should someObjects[42] be hit or should "m_Players[42]"? I would between different types of entities have to translate first from local ID(index) to a global ID then back down to the appropiate entity container.   Another situation for example is when a large number of players or entities are spawned, such that you end up with IDs with values in the thousands. Then later on most of them could be cleared away leaving behind some entities with large ID values. I'm thinking I could be making more problems then I solve.
  5. Hello   I want to know more about how to effectively index players by ID.   Suppose I have a vector that store all players in one place (yes I can break them apart for ECS but for the purpose of the question I have them together): struct player { unsigned int ID; char name[32]; float xpos, ypos, zpos; float xvel, yvel, zvel; int health; // more to come }; std::vector<player> m_Players;  Each player has a unique ID of type unsigned int which can be anything. If there are 6 players and one of them has for some reason has the highest ID value: 9999, then I don't want to have an allocated array of players of size 9999 where 9993 of them are unused to address them directly by ID(myPlayer = m_Players[9999]). I want a array of players of size 6, and I want an array of indices mapping IDs to their position in the player array, such that I can access each player by ID. The reason I don't directly want to use a std::map<id,player> is that I want to effectively traverse the player array in another places. std::unordered_map<unsigned int, unsigned int> m_Index; bool update(const unsigned long ID, float xvel, float yvel, float zvel) { auto search = m_Index.find(ID); if (search == m_Index.end()) return false; auto index = search->second; if(index >= m_Players.size()) return false; auto& myPlayer = m_Players.at(index); if(ID != myPlayer.ID) return false; myPlayer.xvel = xvel; myPlayer.yvel = yvel; myPlayer.zvel = zvel; return true; } void advance(const float timeDelta) { for(auto& myPlayer : m_Players) { myPlayer.xpos += xvel*timeDelta; myPlayer.ypos += yvel*timeDelta; myPlayer.zpos += zvel*timeDelta; } } /* void advance_parallel(const float timeDelta) { parallel_for(auto& myPlayer : m_Players) { myPlayer.xpos += xvel*timeDelta; myPlayer.ypos += yvel*timeDelta; myPlayer.zpos += zvel*timeDelta; } } */  Now, different players can be in different situations, for example, some players might be culled away, some players might be on differnt teams and so on. So I want to know if index maps could be suitable for these purposes: std::unordered_map<unsigned int, unsigned int> m_Index_inactivePlayers; std::unordered_map<unsigned int, unsigned int> m_Index_renderablePlayers; std::unordered_map<unsigned int, unsigned int> m_Index_redTeamPlayers; std::unordered_map<unsigned int, unsigned int> m_Index_blueTeamPlayers; void redTeamHealthBoost(int health) { for(auto& [ID, index] : m_Index_redTeamPlayers) { if(index >= m_Players.size()) continue; auto& myPlayer = m_Players.at(index); if(ID != myPlayer.ID) continue; myPlayer.health += health; } }  Any thoughts, input or comments is much appreciated.
  6. I think I am close to finding what I am looking for. I have to fiddle public, protected and private inheritance to get messages to be accessible after assignment. class WindowBase { public: std::vector<std::tuple<UINT, WPARAM, LPARAM>> m_messages; WindowBase& operator<<=(WindowBase& other) { if (this != &other) { std::swap(m_messages, other.m_messages); } return *this; } }; class WindowMessages : protected WindowBase { public: WindowMessages& operator<<=(WindowMessages& other) { if (this != &other) { std::swap(m_messages, other.m_messages); } return *this; } }; class Window : public WindowMessages { public: Window(); ~Window(); bool initialize(HINSTANCE hInstance); bool postMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); private: HWND m_hWnd = nullptr; HINSTANCE m_hInstance = nullptr; WNDCLASSEX m_wc; }; Window myWindow; WindowMessages messages; messages <<= myWindow;
  7. Hear me out, could I solve this using private inheritance and move assignment overloaded with std::swap? class WindowMessages { public: std::vector<std::tuple<HWND, UINT, WPARAM, LPARAM>> m_windowMessages; WindowMessages& operator=(WindowMessages&& other) { if (this != &other) { std::swap(m_windowMessages, other.m_windowMessages); } return *this; } } class Window : private WindowMessages { public: Window(); ~Window(); bool initialize(HINSTANCE hInstance); // std::vector<std::tuple<HWND, UINT, WPARAM, LPARAM>> moveMessages(); // replaced with move assigment operator in parent bool postMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); private: bool filterMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); HWND m_hWnd = nullptr; HINSTANCE m_hInstance = nullptr; WNDCLASSEX m_wc; }; bool Application::processWindowMessages() { m_windowMessages = m_Window; // WindowMessages messages = m_Window; for (auto& windowMessage : m_windowMessages) { UINT message = std::get<0>(windowMessage); WPARAM wParam = std::get<1>(windowMessage); LPARAM lParam = std::get<2>(windowMessage); switch (message) { //... // Handle messages //... } } return true; }
  8.   Yes, by design. I coded it that way to make it hard for object B to fail because of object A.     C++ provide tools to prevent that, and C++ continues to evolve with new features for this purpose.   It steals the representation of m_messages, leaving m_messages unvalid. I need to swap representations.   If possible, somehow overload the move assignment operator for objectA messages& operator=(objectA&& other) { std::swap(m_messages, other.m_messages); } auto messages = ObjectA; ObjectB += messages; Or perhaps if possible overloading the stream << operator with move semantics. auto result = ObjectC << ObjectB << ObjectA;
  9.   http://puu.sh/sW9MJ/7b018f1c9f.mp4     I have not done testing myself, if there could be situations where tuple would perform better as the post from stackoverflow claims. I will admit I took it at face value.   But it points out much of the problems I have when it comes to programming, which is reading/perceiving conflicting information from different sources. When I read a tutorial and then use that to code my own program I sometimes run into problems. And often the solution does not seem right as I begin to recall information I have read previously. I don't find it very motivating to spend most of my time coding "tricks" just to get things working. So I seek out designs where problems become very localized. I really don't want to worry about object A being in a bad state so that object B causes undefined behaviour.   I don't think users set out to abuse code, they want it working and not breaking. If the string in your example is to be shared with outside code, return a shared pointer.
  10.   So why do we need things private when we can just put a warning label "don't touch" on it? I thought encapsulation  was more about making it hard/impossible to use the class incorrectly. 
  11.     Yes? std::unique_ptr<Thing> p_thingy = std::make_unique<Thing>(); auto refString = thingy->getStr(); //.. // p_thingy is destroyed //.. std::cout << refString.at(i) // oopsie, thingy no longer exist!
  12.   wiki - Encapsulation is the hiding of information to ensure that data structures and operators are used as intended     Tuple is faster than a default struct http://stackoverflow.com/a/40308910, I can take out members that I don't need from MSG, it has move semantics from STL, and I can inline the tuple layout in code.
  13. The above code solve for encapsulation as nothing will hold a reference or pointer to private members. So I can guarantee no dangling pointers or references. I avoid reallocations by moving the vector in and out. The only problem I can think of with that code is a caller might move in a vector that is already gutted from a previous move. I would love to give what I have now a cosmetic makeover and extend it for multiple vectors.
  14.   Look at this code, encapsulation is maintained, no reallocations, no pointers, no references, no consts, no nothing. But it kinda smells, and what if I want swap multiple vectors, I need to pack a tuple.  bool Application::processWindowMessages() { // Temporary relinquish ownership of vector by passing it by rvalue to Window m_windowMessages = m_Window->swapMessages(std::move(m_windowMessages)); for (auto& windowMessage : m_windowMessages) { UINT message = std::get<0>(windowMessage); WPARAM wParam = std::get<1>(windowMessage); LPARAM lParam = std::get<2>(windowMessage); switch (message) { //... // Handle messages //... } } return true } std::vector<std::tuple<UINT, WPARAM, LPARAM>>& Window::swapMessages(std::vector<std::tuple<UINT, WPARAM, LPARAM>>&& messages) { std::swap(messages, m_messages); m_messages.resize(0); return messages; // return by rvalue, giving back ownership }