Marmalade includes different types of API sets, differentiated by the languages used and whether the functionality is cross-platform/generic code or whether it uses platform specific code.
Marmalade supports the following as input languages, and the API sets available to each of these languages can be extended:
Generic cross-platform code vs platform-specific extensions
Marmalade can be extended by either creating platform-agnostic code that only has access to existing Marmalade-supported APIs, or by creating extensions that use platform-specific languages and APIs internally, but expose those to app code via C headers. Creating an extension can give access to useful/new platform frameworks and native UI but will limit support to certain platforms. Cross-platform code by contrast is limited to the APIs Marmalade and its existing extensions already provide, but has the advantage that it will run on any platform.
Extensions & platform abstraction
Platform specific code means code that, for example, uses Objective-C and iOS Frameworks or Java and Android Frameworks. This code forms part of the abstraction layer and is always exposed to applications via C headers. A single C header/API set typically has implementations for multiple platforms, providing a write once-run anywhere API. However, some extensions may support only a few or a single platform. An example would be the iOS Game Center extension which obviously can only work on iOS.
When creating API sets using platform specific code, you use the platform's native toolchain to create some sort of library, which will be exposed by C headers to Marmalade users. A Marmalade user's actual C++ application code always uses Marmalade-specific toolchains and libraries and has no direct access to native SDKs (iOS SDK, Android SDK, etc.)
There are two ways platform abstraction APIs are exposed in Marmalade:
- Through the loader:
- Each platform has a pre-built component that ships with Marmalade.
- This implements core abstraction APIs and also does things like view/window setup, event marshalling and loading the user's app code (hence the name).
- The APIs implemented in the loader are core things like memory, file, Open GL ES, audio, touch, etc.
- Through extensions:
- Extensions are sets of libraries with a shared header that expose additional platform/system functionality.
- These work similarly to the loaders but have the advantages that:
- They are optional - if you don't want to use an API then it wont bloat your app size.
- They can be closed or open source - open source versions allow other users to improve, bug fix and add back-ends for new platforms for you.
- We ship a set of extensions, both our own and trusted third party ones, inside the SDK. Extensions are also available from 3rd party websites and github.
- You can create your own extensions using the EDK, which ships with Marmalade. This is a typical way for service providers to expose their existing SDKs for iOS, Android, Windows, etc. to Marmalade users.
In the early days of Marmalade, we would occasionally add new APIs to the loaders; these days, when we create new system level API sets, we just add them as extensions and often include source code. The Platform abstraction layer (S3E) API reference docs include all of the loader and extension based APIs that ship with the SDK. From a users point of view there is little difference between the two apart from that they need to include
subproject extensionname if the API is actually an extension.
The extension architecture allows for an extension to not be implemented on any given platform. Extensions provide a built-in
MyExtensionAvailable() function which can be used to check if a platform supports the extension or not. If an extension is not supported on a platform, it can still be safely used in code, compiled and called on device. Any functions from an extension that aren't supported will simply return immediately with a value of NULL, 0, false, etc.
You can build your own extension on supported platforms using the Extensions Development Kit (EDK).
For Marmalade to make use of a library like this, the code needs to only use function supported by Marmalade and it needs to have been compiled using a toolchain that Marmalade supports. Libraries should be built for every architecture combination that needs supporting, for example ARM GCC, Windows X86, Android x86, etc.
See C++ libraries in Marmalade for how to use, create and build libraries.
When using Marmalade Juice, libraries can be built in the same way, but also can use Objective-C source (.m) or Objective-C/C++ (.mm) You may want to take an existing C++ library and wrap it with an Objective-C interface. Juice projects support C/C++/Objective-C code and so can use C++ middleware and C extensions out of the box.
When using Marmalade Quick, since Lua is normally compiled at runtime, there is no concept of a library in Lua in the way that there is in C++. However, quick projects also use the MKB project system, so you can use MKF subprojects to group and add sets of LUA files similarly to creating and adding C++ open source subprojects. You can use Lua's
require("MyMainPackageFile") function to include a group of LUA files and treat them like a library (require works a bit like #include, or more accurately #import). If you want to avoid providing source to your Lua code, you could pre-compile using the Lua Compiler (luac) and then tell users to use Quick's precompiled Lua option when deploying final apps.
Quick also supports creating Lua wrappers/interfaces to C++ libraries using a mechanism called tolua++. Out of the box, a number of Marmalade's abstraction APIs are exposed as Lua APIs for Quick, but you can expose any further ones - both extensions and C++ middleware - by using tolua++. If you are creating a new C/C++ integration of any sort, please consider also creating a Lua wrapper for Quick users.
Components of Marmalade and how they can be extended
Components are typically defined by marmalade subproject files (MKFs). They use the same syntax as Marmalade MKB projects and can be included from MKBs or from other subprojects using the
Depending on the type of component, a different mechanism will be used to build the component, but that is all automated by Marmalade's MKB toolchain.
|Language||Component type||Description||Built using||Open/closed|
|Platform languages (C++/Java/Objective-C/C#/etc.)||Loaders||The pre-built components that implement core abstraction layer functionality, exposed via C headers.||Ships with SDK. Users cannot edit or rebuild these.||Closed source.|
|Extensions||Modular extensions to the abstraction layer. Can have one or more platforms supported for each API.||Extension Development Kit (EDK).||Closed or open source.|
|C++||C++ module||Cross platform C++ libraries and code bundles. These can depend on extensions underneath and therefore still have platform restrictions.||Regular Marmalade MKB project and toolchains, but with "library" as the target type.||Closed or open source.|
|Objective-C||Objective-C module||Cross platform Objective-C/C++ libraries and code bundles. Identical to the C++ version but built with the Juice/CLANG toolchain, with support for Objective-C and some iOS frameworks.||As above, but with CLANG as compiler.||Closed or open source.|
|Lua||C++ to Lua wrapper||Lua components that use "userdata" types to bind lua calls to C++ calls. These extend Quick to support additional APIs from C++ Marmalade. Typically you would create a C extension and then wrap it for Quick with toLua++.||toLua++ script generates C-to-Lua bindings which can then be customised.||All lua code shipped in Marmalade is provided as source. Lua code is typically provided as source, but you can optionally pre-compile.|
|Lua module||Lua code can also simply be added to an MKF package if needed.||No build steps required, just include files in an MKF.|
How components are built and included
Including any component
All component types apart from the loaders themselves usually have a .mkf subproject file that defines them. For example, the IwUI module has an IwUI.mkf file. An MKF file is similar to a project's MKB file in that it specifies files, defines, build options etc. but it is used only to include components and not to build them.
MKB/MKF files for projects or other components would include IwUI by including the command
subproject iwui (no .mkf extension).
C++ code may be included as either source or library versions in a subproject. For libraries, the module would also have its own MKB file, which is run in order to build the lib files that the MKF includes. See MKB > Including dependencies on other code modules for detailed info on using MKF files.
Extensions are built using the Extensions Development Kit (EDK), which is integrated into the regular Marmalade MKB toolchain. Essentially, you specify the header that you wish to expose to apps - using a .s4e file - and the EDK then generates utility wrappers, MKF/MKB files and source files with empty stub functions. You then write the implementation code into these, using native platform code and Marmalade helper functions.
To generate Lua wrappers for C++ APIs, you run tolua++ over the C++ code and can then add additional implementation code to the output. See Extending Quick. New Quick/Lua APIs are then just included with regular commands like
Best practice: extension, cross-platform or both?
If you have some code or a library that you wish to expose to Marmalade users, you should consider how best to do it. You may have the option of creating multiple extensions per platform, a single extension with different platform back-ends, a cross platform library or a hybrid approach. Sometime an extension may be very easy to implement - for example an Android extension might just extends an activity and have no code wrapping at all. Often the hybrid approach is a good idea for code management and extensibility.
Lets say you have an existing iOS and Android SDK for your service that includes nice native UI elements in each case, plus a generic REST web API that can be used on any platform.
- You could chose to not write an extension at all! Marmalade supports HTTP(S), so users could use the REST API directly and it would be very cross platform. The down side of this is that many C++ users would prefer a C++ API and they lose access to the native UI and any other cool platform features your SDKs have.
- You could just use the EDK to create separate extensions for iOS and Android, with C functions that closely match the Objective-C or Java APIs in each case. This may be quick, but means users have to call different code for Android vs iOS in their apps. Marmalade users hate doing that!
- You could write a single extensions (one .s4e header spec) and then write back-ends for Android and iOS using the same API signatures. This is usually the best approach for an extension.
- However, sometimes writing multiple extensions and then unifying them with a C++ subproject on top is a better idea. This is usually appropriate for complex modules that unify similar but not identical platform APIs. IwBilling module is a example of this. That has separate extensions for each store, some of which were written by different teams at different times and each of which exposed the full capabilities of its store. The IwBilling C++ part then exposes the shared functionality via a single API. Advantages of this are:
- The C++ part can use higher level and OO code to provide a more powerful and easy to use interface that pure C can.
- The individual extensions could be created separately and quickly without worrying about the need for a final unified design.
- The individual extensions are not affected by any limitations of trying to homogenise the APIs.
- The whole thing is more modular and easier to maintain.
- For a really hybrid approach, you could have an extension that uses the platform code and native UI for specific functions on iOS and Android, and then on top of that have a more flexible C++ API that exposes the REST APIs through C++ and has a skinned UI fallback for the parts that the Android and iOS SDKs take care of.
- Alternatively, if you're Android and iOS SDKs are just platform wrappers around shared C++ code that you don't normally make public, does it make more sense to just wrap that in a marmalade library and provide a truly cross platform solution?
- Once you have your C++ extension/library/hybrid, consider exposing it to Marmalade Quick or Marmalade Web. Marmalade Quick currently has a more limited 3rd party ecosystem (less ad providers, less analytics providers, etc.) than Marmalade C++ and it is accessible to less experienced uses. Therefore, providing your API as Quick-ready out of the box may be an easy win for attracting new users to your services.
Finally, tell us about your integration! firstname.lastname@example.org