x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

How can I set up multiple modules so that they can interact?

I'm creating a plugin that has at least two modules. One will be responsible for managing data and the other will be responsible for the editor interface. Here is where I'm running into issues. I can't quite wrap my head around what the hierarchy should be as far as the module interfaces and class structure go.

Here is a quick break down of how I have things right now (I can provide more details if necessary):

 +Project
    + Plugins
       + MyPlugin
          + Source
             + DataModule
                + Private
                + Public
             + EditorModule
                + Private
                + Public

Each module has it's own Build.cs and PrivatePCH.h file. The data module (the main module) has the following .h which is then included in the PrivateDependencyModuleNames array in the Editor's Build.cs file.

 #pragma once
 
 
 /* Dependencies
  *****************************************************************************/
 
 #include "CoreUObject.h"
 #include "ModuleManager.h"
 #include "Settings.h"
 
 /* Common
 *****************************************************************************/
 
 #include "Common/DataEntity.h"
 
 
 /* Interfaces
  *****************************************************************************/
 
 #include "Interfaces/IDataEntityManager.h"
 #include "Interfaces/IDataModule.h"


From looking at multiple examples it looks like it is ideal to have a basic Interface for each module that is a subclass of IModuleInterface, and then subclass that interface w/ a class that is private. So, that has been the approach I took.

Here enters the problem. I need to access some of the FDataEntityManager (my subclass of IDataEntityManager) functions from within the Editor module. The question I would like to ask, is what is the best way of doing that? Right now I can "see" the IDataEntityManager and get a shared pointer to it via a call to the function IDataModule::Get().GetDataEntityManager(), but that doesn't help me access to the FDataEntityManager's public functions, much less "see" them. What am I missing here?

Product Version: Not Selected
Tags:
more ▼

asked May 05 '14 at 10:35 PM in C++ Programming

avatar image

TheAggie
48 4 8 10

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

1 answer: sort voted first

Hi Aggie,

Your directory hierarchy looks great, so the question really comes down to how to structure the public interface of your modules. The answer to this depends on what the role of each of the modules is and how much you want to decouple the module implementations from each other.

Most modules will fulfill one of two roles: they can be a plug-in that extends some existing functionality, or they can be a library that provides new functionality. The difference is subtle, but important. In the first case your module will be the consumer of public interfaces exposed by others (i.e. the Engine), and in the second case your module will provide public interfaces to be consumed by others. The direction of who provides and consumes the interfaces is reversed.

When a library module provides public interfaces to be consumed by others, there are different ways in which the public interface can be implemented, and these ways generally differ in the degree of coupling to the module's implementation details. The easiest option is to treat such a module as a static library that directly exports new types, such as FDataEntityManager. Other modules can add a module dependency to this module, include its public header file and then immediately create instances of FDataEntityManager. For many library modules this is sufficient.

The downside of such static libraries is that they strongly couple the consuming modules to a particular type of implementation, i.e. FDataEntityManager. This means that, if you ever wanted to swap out this class for another implementation in the future, i.e. FBetterDataEntityManager, then you would have to rewrite parts of the consuming module. If you anticipate that you may have different kinds of data entity managers down the road, then it would be advisable to expose this functionality with an interface class instead, i.e. IDataEntityManager.

Library modules that expose their implementations through interface classes have the advantage that particular implementations of these interface classes are no longer visible to the outside world, which means that they can be exchanged without affecting consumers (Liskov's Substitution Principle). In other words: interface classes loosely couple the consuming modules to the module providing the functionality. For example, the FDataEntityManager class would implement the public IDataEntityManager interface class in the Private part of DataModule, and EditorModule would consume the IDataEntityManager interface classes that are returned by the DataModule instead of instantiating FDataEntityManager instances itself.

When you make implementations available through interface classes then you need a mechanism to create actual instances of types that implement those interface classes. For example, if FDataEntityManager is a private implementation detail of DataModule, then EditorModule can no longer do something like DataManager = new FDataEntityManager(); because it cannot see the type FDataEntityManger. The most common mechanism used is the Factory Method Pattern in which the DataModule would expose a method in its interface that allows for creating instances of data entity managers, i.e. IDataModule::CreateDataEntityManager().

The module's public interface, i.e. IDataModule, is also an abstract interface class, so you may wonder how we get an actual instance of the module implementation. This can be accomplished by requesting the module instance from the ModuleManager, i.e. FModuleManager::LoadModuleChecked<IDataModule>("DataModule");


To make a long story short, there are different ways of exposing the functionality of library modules, the most common of which I have outlined above. You have to make a decision about the degree of coupling between your modules you would like to achieve. If you will ever only have one data entity manager implementation, then the easiest way would be to simply expose the FDataEntityManager type in the public interface of DataModule. In this case you do not need any interface classes at all.

If you want the implementation details of DataModule to be decoupled from EditorModule, then you should expose all types in the form of interface classes. This is more work, but will provide better flexibility down the road, if you need it. In the Unreal Engine code base we use both approaches, and sometimes we even mix and match, which I would strongly recommend against, however.

Last but not least, I would like to add that your EditorModule probably does not require a public interface at all, because it acts as a plug-in, not a library. But it looks like you already figured that out.

more ▼

answered May 06 '14 at 02:11 PM

avatar image

gmpreussner STAFF
4.6k 125 61 233

avatar image TheAggie May 08 '14 at 01:47 AM

Thank you very much for the detailed answer! That was quite helpful!

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question