Advertisement Jump to content
  • Advertisement


  • Content Count

  • Joined

  • Last visited

Community Reputation

2413 Excellent


About Zipster

  • Rank

Personal Information

  • Role
    Technical Director
  • Interests

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. What is this fixation with allocating from the stack? Better solutions that use standard, portable C++ have been provided. The dangers and pitfalls of dynamic stack allocation have been amply explained and demonstrated. No extenuating circumstances have been presented, and even if they had it's highly doubtful stack allocation would be the appropriate solution. It's clear from contextual clues that the OP is struggling with the basics and just needs something that works so they can move on. I don't see how this could be more cut and dried. That's besides the point. You can't detect how much stack space is remaining, nor can you even attempt an allocation without overflowing should there be insufficient space. Essentially, your application may or may not crash depending on conditions you can't detect, and there's nothing you can do about it. This is the type of code that should be rooted out, not endorsed.
  2. There's no such thing as the compiler choosing an overload based on whether or not it compiles; at that point, the best overload has already been chosen and compilation errors are what they are. SFINAE applies specifically to substitution errors, while the compiler has to determine the actual types for template arguments so it can create the candidate set for overload resolution. Take a look at this example which does what you need: The reason this works is because std::enable_if will only have a member typedef 'type' if the condition is true. So in the cases where the condition is false, "std::enable_if<false,T>::type" will be considered a substitution failure because the compiler can't find 'type' and perform the argument substitution. However, you intentionally provide two overloads such that one will always succeed and the other will always fails (based on the return type), so the compiler will always be able to generate a candidate set with at least one method (depending on what other overloads it finds).
  3. I would start with the idiomatic, iterator-based approach and simply add a convenience method for containers and other "iterable" types: template <typename InputIt> std::string join(InputIt begin, InputIt end) { std::string result; /* does the thing */ return result; } template <typename C> std::string join(const C& c) { using std::begin; using std::end; return join(begin(c), end(c)); }
  4. You could start with a basic implementation where component factories are registered by name in a map. When creating your entities, iterate over the "components" object and use the key to look up the factory in the map. Then pass the corresponding value to that factory so it can instantiate the component with the proper data. As there's nothing with this approach specific to components or entities, it can be reused and extended to support just about any structured, nested data model. This technique is commonly used in serialization libraries, where you frequently need to reconstruct runtime data from information received over the network, or some other external source.
  5. Zipster

    Symbol lookup error on Linux

    CMake is more than capable of handling the build configuration you describe, provided that the target dependencies have been correctly declared. The two most likely causes of your issue are 1) libLinearMath.a isn't being linked in (however this would probably result in a lot of other unresolved symbols), or 2) libLinearMath.a is being linked in before whichever object uses the symbol. Either way, it points to a fixable configuration problem. Is the code that calls CProfileManager::Reset() inside a translation unit that's linked directly into the shared library, or is it in another static library (or perhaps shared library)? How have you configured your target dependencies (i.e. [TARGET_]LINK_LIBRARIES)? Also next time you build, use "make VERBOSE=1" for it to echo all the commands it executes so you can observe the link line it generates.
  6. Zipster

    c++ use map with struct

    The best approach really depends on who you expect to be consuming this data. You have to be really careful when using standard library types such as std::string across binary boundaries, or anything that has a layout that can change between platforms and/or build environments. Even if you take all the necessary steps to ensure these are identical, only C++ code knows about std::string. Perhaps this isn't an issue if all your software components are written in C++, which is why so much of this depends on your intent
  7. The handles can go directly into any standard library container without the need for a custom allocator: extern IObject* createObject(); std::vector<ObjectHandle> handles { ObjectHandle(createObject()), ObjectHandle(createObject()), ObjectHandle(createObject()) }; The behavior of your "defragmenting" heap is different from how the language and standard library expects storage to behave, so while I'm sure that you could find a way to wrap object allocation in an allocator, it wouldn't do you much good if it can't be used anywhere. At least with this approach, you retain full control over your heap while still being able to use standard containers for the handles.
  8. When an object is moved, any references to it must be updated with the new location. The "handles" register themselves with the object so that for the duration of their lifetime, they can be informed of any such relocation. But aside from this registration logic, they're just simple wrappers around naked pointers that implement a few operators so they behave "pointer-like" to a user.
  9. Imagine that you were designing this as a pen and paper game. No type systems, instances, or other computer language constructs. How would your "rule" system work then? How would you describe to a human player whether or not their character is allowed to use some weapon (or object in general)? Choosing a specific example, let's say you tell them that swords can only be used by warriors. What questions might they have about this rule, and how would you answer them?
  10. This isn't a problem to be solved by the allocator. With the appropriate handle semantics, I don't see any reason why this shouldn't be possible: class IObject { public: virtual void Bind(IObjectHandle* handle) = 0; virtual void Unbind(IObjectHandle* handle) = 0; }; class IObjectHandle { public: virtual void Rebind(IObject* object) = 0; }; class Object : public IObject { std::set<IObjectHandle*> m_handles; public: Object(const Object&) { } Object(Object&& other) { m_handles = std::move(other.m_handles); for (auto&& handle : m_handles) { handle->Rebind(this); } } ~Object() { assert(m_handles.empty()); for (auto&& handle : m_handles) { handle->Rebind(nullptr); } } Object& operator=(const Object&) { return *this; } Object& operator=(Object&& other) { m_handles = std::move(other.m_handles); for (auto&& handle : m_handles) { handle->Rebind(this); } return *this; } void BindHandle(IObjectHandle* handle) override { m_handles.insert(handle); } void UnbindHandle(IObjectHandle* handle) override { m_handles.erase(handle); } }; class ObjectHandle : public IObjectHandle { IObject* m_object = nullptr; public: ObjectHandle(const ObjectHandle& other) { m_object = other.m_object; if (m_object) { m_object->BindHandle(this); } } ObjectHandle(ObjectHandle&& other) { if (other.m_object) { other.m_object->UnbindHandle(&other); other.m_object = nullptr; } m_object = other.m_object; if (m_object) { m_object->BindHandle(this); } } ObjectHandle(IObject* object) { m_object = object; if (m_object) { m_object->BindHandle(this); } } ~ObjectHandle() { if (m_object) { m_object->UnbindHandle(this); } } ObjectHandle& operator=(ObjectHandle handle) { handle.swap(*this); return *this; } void RebindObject(IObject* object) override { m_object = object; } operator bool() const { return m_object != nullptr; } IObject* operator->() const { return m_object; } IObject& operator*() { return *m_object; } const IObject& operator*() const { return *m_object; } void swap(ObjectHandle& other) { if (&other != this) { if (m_object) m_object->UnbindHandle(this); if (other.m_object) other.m_object->UnbindHandle(&other); using std::swap; swap(m_object, other.m_object); if (m_object) m_object->BindHandle(this); if (other.m_object) other.m_object->BindHandle(&other); } } }; Note that most of the heavy lifting happens as part of the move and swap semantics. The copy semantics of the object are also stubbed out, since handles are uniquely bound to a single object. However I also have to question why this is needed in the first place. If fragmentation is a real concern, it's usually a symptom of another problem like sub-optimal allocation strategies or memory usage patterns.
  11. Most C# developers would probably expect an exception to be thrown. Not only is this an exceptional case, but it's also how the standard library handles it. So when in Rome... stick with established best practices Also, consider the fact that games usually load files that are necessary for proper functionality and expected to exist (assets, etc.), based on the assumption that the game was properly installed. There isn't much the software could do to recover and continue from missing assets, and as a developer I wouldn't want my game to gimp along with missing data anyway!
  12. Could you elaborate or provide an example of what you're looking for? It isn't clear if you're using the term "combination" literally, or colloquially to mean some other type of arrangement.
  13. LEB128 and related encodings will let you send any size integer, but for now you should probably just stick with a fixed size like 4 bytes until everything is in a working state.
  14. I would use a light-weight wrapper object that implements operator bool() and operator->() at the very least, so you get pointer-like semantics and usage with the option to add behavior as necessary. For instance, you could make the copy constructor explicit so users can still copy the handle, while also allowing one to easily locate such copies through static analysis. Or if you're feeling clever, you could even add tracing/logging to copies, access, etc., so if/when something goes wrong, there's a "chain of custody" so to speak that let's you identify errant usage. Anything like this can also be compiled out in production builds so there's almost no overhead.
  15. Zipster

    Allocator design issues

    This wouldn't change the interface. The purpose of "Deallocate" is to inform the allocator that the memory is no longer in use, at which point it can do with it what it pleases (including nothing at all). If the user is expected to know more about the underlying implementation details, you have a leaky abstraction. Take a look at the standard allocator traits. The essential elements of the interface are allocate/deallocate, and construct/destroy (notice how they're considered separate responsibilities). You really don't need much beyond that, aside perhaps from some templated helper functions that combine allocate+construct and destroy+deallocate. Then, different implementations of this interface can be combined using the adapter pattern to achieve the layered functionality you're looking for.
  • Advertisement

Important Information

By using, you agree to our community Guidelines, Terms of Use, and Privacy Policy. is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!