Jump to content
  • Advertisement

Matt Green

Member
  • Content Count

    134
  • Joined

  • Last visited

Community Reputation

152 Neutral

About Matt Green

  • Rank
    Member
  1. Here is a prior discussion that might be useful to you. For interactions that can't be handled by events, I use interface classes.
  2. Also consider WTL. Bit more refined than MFC, and has no run-time requirements. EDIT: MFC bloat is a drop in the bucket these days when you have things like every single object in the .NET wasting four bytes on a Monitor. :)
  3. Look up how something like COM works. Components programming is the only way to do this nicely. For settings, you'd have an interface class and then an implementation class. Plugins get an instance to the implementation class by passing the interface class to a registry that maps the interface class to the implementation class. if(ISettings* settings = _registry.Get<ISettings>()) { _volume = settings->Get<int>("volume", 0); _music = settings->Get<bool>("music", false); }
  4. Matt Green

    Headphones..

    Get a decent pair of Sennheiser open headphones. Open headphones tend to sound more natural, but leak out more sound. Closed headphones block out more of the world, but don't sound as nice. I'd start with the PC161's. They have some great sound and are only $90 or so. It also has a mic attached for VOIP. If you like them you can delve deeper into audiophile-esque world. Try head-fi.org for more info. EDIT: didn't see the wireless part. I know they make wireless headphones, so look around.
  5. 'GSNT' is a constant expression. Try it.
  6. If you want to guarantee unique subjects, you can always use GUIDs. They are 8 bytes though, and can take much longer to compare. Another alternative is to use char pointers. I like this the best because debugging is simple, you can still switch on them, and the user doesn't have to worry about trampling the system ones. Either way it sounds good. If you use an enum and cast it to int you lose some info that can be used during debugging, so I started thinking along those lines.
  7. Use CreateProcess() function with CREATE_SUSPENDED in the fdwCreate parameter. Do your injection, then call ResumeThread() to let the child process actually start.
  8. Quote:Original post by gameXcore Does anyone have any suggestions or bright ideas, I've spent the last couple of days trying to come up with the golden solution, but nothing works, or feels right. I'm thinking the solution is probably going to involve some pre-processor code generation magic. Let me be more concrete. We'll use the FastDelegate library to do this. If you want, you can use boost::function and boost::bind. I find FastDelegates do less damage to compile times, so I prefer them for this sort of thing. boost::bind is far, far more flexible, however. Also, keep in mind that where you say messages, I say events. I define an Event as follows: struct PlaybackStartedEvent { PlaybackStartedEvent(Track* track) : Track(track) { } Track* Track; }; Note the lack of a base class. You can have as many fields as you want in the event. It carries all the data that is needed to update other system components when a track has started playing. Now, this data is useless without a way of notifying everyone about it. So, let's do that: #include <algorithm> #include <map> #include <vector> #include <cassert> #include "Delegate.hpp" class EventBus { public: ~EventBus() { for(HandlerTable::iterator i = _table.begin(); i != _table.end(); ++i) delete i->second; } public: template<typename Event> void Publish(const Event& e) { Handlers* handlers = this->GetHandlers<Event>(false); if(! handlers) return; Delegate<void (const Event&)> target; for(Handlers::iterator i = handlers->begin(); i != handlers->end(); ++i) { target.SetMemento(*i); target(e); } } template<typename Event> void Subscribe(void (*target)(const Event&)) { assert(target != 0); if(target == 0) return; Handlers* handlers = this->GetHandlers<Event>(true); handlers->push_back(Delegate<void (const Event&)>(target).GetMemento()); } template<typename Class, typename Event> void Subscribe(Class* instance, void (Class::* target)(const Event&)) { assert(instance != 0); assert(target != 0); if(instance == 0 || target == 0) return; Handlers* handlers = this->GetHandlers<Event>(true); handlers->push_back(Delegate<void (const Event&)>(instance, target).GetMemento()); } template<typename Event> void Unsubscribe(void (*target)(const Event&)) { assert(target != 0); if(target == 0) return; Handlers* handlers = this->GetHandlers<Event>(false); if(! handlers) return; Handlers::iterator i = std::find(handlers->begin(), handlers->end(), Delegate<void (Event&)>(target).GetMemento()); if(i == handlers->end()) return; handlers->erase(i); } template<typename Class, typename Event> void Unsubscribe(Class* instance, void (Class::* target)(const Event&)) { assert(instance != 0); assert(target != 0); if(instance == 0 || target == 0) return; Handlers* handlers = this->GetHandlers<Event>(false); if(! handlers) return; Handlers::iterator i = std::find(handlers->begin(), handlers->end(), Delegate<void (Event&)>(instance, target).GetMemento()); if(i == handlers->end()) return; handlers->erase(i); } private: typedef std::vector<Internal::DelegateMemento> Handlers; typedef std::map<const char*, Handlers*> HandlerTable; private: template<typename Event> Handlers* GetHandlers(bool createIfNotPresent) { const char* key = typeid(Event).raw_name(); HandlerTable::iterator i = _table.find(key); if(i == _table.end()) { if(! createIfNotPresent) return 0; Handlers* handlers = new Handlers(); handlers->reserve(4); // wild-ass guess. std::pair<HandlerTable::iterator, bool> result = _table.insert(std::make_pair(key, handlers)); i = result.first; } return i->second; } private: HandlerTable _table; }; The implementation is a bit long, but it isn't too bad. The EventBus maintains a table of event handlers. These are functions the client has requested should be called when a particular event occurs. In the EventBus parlance above, the Event is Publish()'d to everyone who has Subscribe()'d to it previously. The Subscribe() and Unsubscribe() methods each come in two flavors: free (non-member) and member functions. They're just syntactic sugar and conceal the fact that the FastDelegate class is used to hold the event handler. Internally, a map is maintained that converts the message type to the list of handlers. FastDelegate has a so-called "memento" feature that we exploit. It lets you destroy the concrete type information associated with the callback and store a generic function pointer. The caveat is you have to cast it back to the correct type before you use it. This may seem useless, but it lets the EventBus class store delegates without caring what the exact type of the argument is. We just need a way to restore that type when we go to use it. Luckily, C++ is up to the task. The GetHandlers() method is templated on the Event type. This template parameter is carried through by the public methods. However, somewhat surprisingly, inside of GetHandlers(), we turn around and use RTTI on it. The reason for this is RTTI can (usually!) give us something to use as unique key for this particular type. Look up the std::type_info object if you aren't familiar with it. The name() field can serve as a unique identifier for this particular type! Knowing this, let me be a downer and mention that this code is tailored toward MSVC in two ways: 1. it uses std::type_info::raw_name() instead of std::type_info::name(). This is because the latter tends to generate false positives in the VC memory leak checker. If you're not on MSVC, feel free to substitute name() with no ill effects, assuming... 2. it relies on the fact that each of the raw_name() should point to one (and only one) unique name for each type (hence using the pointer as a key). IIRC, the standard doesn't require implementations to honor this requirement. I think only the most cheap-ass implementation would skip providing type names. However you go about it, the problem is tricky to solve in C++ (as you discovered). You need to mix a compile-time construct (templates) with a run-time one (RTTI) in order to get something that is elegant. RTTI is preferable to requiring users to hardcode a key because it means the event class doesn't need to fulfill any sort of contract. In more dynamic languages, this sort of construct is much easier to implement. Anyway, let's see how it's actually used, first from the handler side: class UIComponent { UIComponent(EventBus* bus) { // Tell the bus we're interested in receiving these events. // The event type is inferred from the argument list of OnPlaybackStarted. bus->Subscribe(this, &UIComponent::OnPlaybackStarted); } void OnPlaybackStarted(const PlaybackStartedEvent& e) { _presenter.OnPlaybackStarted(e.Track); } When some other component calls Publish() with a PlaybackStartedEvent, UIComponent::OnPlaybackStarted will be called with the instance of PlaybackStartedEvent that was passed to Publish(). And now from someone who wants to Publish() the Event: // Passing events as const-ref allows us to create and fire in a single line: _bus->Publish(PlaybackStartedEvent(currentTrack));
  9. I'd agree with you Buckeye, but I recently ran into an issue where the behavior of a common control was not at all what you'd expect, and it took a GDI leak detection tool to confirm it, and also confirm that the fix was correct. I will check at home. This is a quick starting point for the OP: http://msdn.microsoft.com/en-us/magazine/cc188782.aspx
  10. Use a message struct, and use the std::type_info as the subject.
  11. TheBuzzsaw, I think you will like that setup. I use it in my application. The only tricky thing is handling things when they are done -- if this is necessary. Since I'm working on a GUI app, getting the results to the GUI thread from the worker thread was kind of a pain. But, otherwise, task-based parallelism is great. Look up Java's Executor framework to get an idea of various features that exist. Implementation-wise, if you're on Windows, you can use I/O completion ports to actually implement the queue. Writing a good work queue on Win32 is not a trivial task due to the lack of condition variables. Larry Osterman has a blog entry about this that demonstrates I/O completion ports. Alternatively, you can use a thread message queue. On Linux or OS X, you can use a condition variable when writing the queue.
  12. This microkernel approach is the same I'm taking on a recent project of mine. If you dig up my posts from wayyyyy-too long ago, I talked on it some. My current design is more component-oriented than object-oriented: * Every major subsystem is a Component: the library, the playback controller, the UI, etc. * Components can discover other components *if* they implement a well-known interface. At initialization time, each component is given a context that contains a reference to the ComponentManager and the EventBus. Direct component-to-component communication is only used when a method call makes more logical sense than using events... * Which leads us to the push mechanism: the EventBus. This is similar to signals and slots but you do not know who is generating the event (IIRC, the signal lives somewhere. If this is not the case, disregard it.) The EventBus maintains a map of std::type_info objects to handlers. It also verifies that you are sending Events from the UI thread. This is inspired heavily by enterprise-level messaging middleware. * A rich set of Events are created: PlaybackStartedEvent, PlaybackStoppedEvent, TracksAddedEvent, etc. These are PODs with a couple of relevant fields. (By now, you can tell what type of application I'm writing, just from the events.) I can't say enough good things about the architecture. The coupling is low, you can instrument the EventBus to quickly debug things. Well-known events and interfaces are kept in a central location. The author of Imperfect C++ has a chapter entitled "Objects Across Borders," where he attempts to use DLLs from one compiler with another, and documents the difficulties he encounters. This sounds similar to what you want to do. Memory management can be difficult. I have done something like this on a previous project, but it can be tedious. I went the common DLL route as well, but I dislike it.
  13. I'm wondering if anyone has any experience with doing research in programming languages/compilers? I'd like to eventually end up doing research at a Google or MS, and am looking for grad schools that you'd recommend. So far I have been working off of the Top 20 list at USNews and World Report, but maybe you have some other ideas. I have a bachelors in CS from Virginia Tech, and over 5 years of industry experience. I don't know if I can get into a CMU or MIT at this stage, however. (Nevermind the fact that those two places don't even seem to offer a general master's degree in CS.)
  14. Memory-mapping sounds like the way to go here. The OP can fine-tune the search algorithm if they wish by using something in place of strstr, but I doubt that's necessary. The only concern is whether there's enough unfragmented address space available to the process. You should be alright at 1 GB. Memory-mapping is one of the coolest tools available on most OSes and you never hear anything about it.
  15. Matt Green

    Messaging Pattern

    Maybe I can be of some help. Event.hpp: template<typename DerivedEvent> struct Event { template<typename Class> static EventBinding Bind(Class* instance, void (Class::* function)(DerivedEvent&), EventHandlerPriority priority = Normal) { const wchar_t* name = DerivedEvent::GetName(); EventHandler handler(instance, function, priority); EventBind(name, handler); return EventBinding(name, handler); } static EventBinding Bind(void (*function)(DerivedEvent&), EventHandlerPriority priority = Normal) { const wchar_t* name = DerivedEvent::GetName(); EventHandler handler(function, priority); EventBind(name, handler); return EventBinding(name, handler); } void Invoke() { EventInvoke(DerivedEvent::GetName(), static_cast<DerivedEvent*>(this), DoInvokeHandler); } template<typename Class> static void Unbind(Class* instance, void (Class::* function)(DerivedEvent&)) { EventUnbind(DerivedEvent::GetName(), EventHandler(instance, function)); } static void Unbind(void (*function)(DerivedEvent&)) { EventUnbind(DerivedEvent::GetName(), EventHandler(function)); } private: static void DoInvokeHandler(void* event, const EventHandler& handler) { Delegate<void (DerivedEvent&)> target; target.SetMemento(handler.Target); target(*static_cast<DerivedEvent*>(event)); } }; I wanted client code to look like this: ShutdownEvent::Bind(this, &ClientComponent::OnShutdown); // ... void ShutdownEvent::Bind(ShutdownEvent& e) { } It is straight-forward and easy to use. No down-casting or other silliness. And I was willing to move heaven and earth in order to accomplish that. There were a few issues I encountered. The first one is that I wanted to keep a master list of event handlers in one place in the engine, despite the fact that every event handler would have a different signature (the first argument would be the event they are interested in) and wouldn't have a common base class. (Now, I could require that every implementor of subclassed events have their own list of handlers, but I remained stubbornly optimistic and dismissed this as less elegant.) It was important to me that the client code did not downcast the event when it received it, too. I wanted no unnecessary code, no noise required to get down to business. (I am extremely lazy, as you will find out.) Thus, the master list of event handlers would require subverting the type system a bit. After all, you can't exactly store function pointers without calling out their exact argument types. Well, that is where I was wrong. After I learned way too much about function pointers, I stumbled upon the FastDelegate library. It provides a GetMemento() function that returns an opaque representation of the delegate...be it a function call, or a method call. So I had a way to obscure the physical type of the object without polymorphism and the associated dynamic allocation requirements. I could store the DelegateMemento type in my handler list. So inside of the engine, I keep a hash table of event names (ShutdownEvent) to a prioritized list of event handlers. This led me to my second problem: how do I resurrect the type information? I just went to all that trouble to throw it away, and only then realized the real value of having it around - it makes the data usable! This point stumped me endlessly until I read about the Curiously Recurring Template pattern. Some time later I realized it was possible to use CRTP to bring the type information back. Look at the definition of the Invoke() method. It calls the engine's Invoke function, passing down the event name, a downcasted version of the this pointer, and a function pointer used to call the handler. The engine calls DoInvokeHandler once for every event handler that is registered. The SetMemento method takes the opaque method call and makes it usuable again. We use the type information that CRTP gives us to synthesize the correct type information for the function call and the argument. Finally, we call the function. This was easily the most intense C++ hackery I'd ever done. I have not profiled it extensively. You're guaranteed that there is at least one indirect method call - the call through the delegate. How the DoInvokeHandler call is handled is up to your compiler - I'm almost certain it would also be indirect, but I have not taken the time to examine this. But I am more than fine with the performance of it, given the amazing amount of readability it confers on the code. I am considering releasing it as a library of header files for people to examine and work with. It is close to what you are looking for Toohr, but I wrote this lengthy discussion of it to get the ball rolling in your head so you can continue refining it to your needs. I've only scratched the surface.
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net 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!