How to install tools to develop Symbian Application?
Here we only use the lastest version (at this time) of the tools.
If you are the C++ developer:
That's all you need. Let's begin to write the program.
This article aims at providing an introduction to OpenGL ES for someone who is already experienced with desktop OpenGL, and shows how to use it with S60.
Open file openglestestAppView.h, add a function to initiate the OpenGL ES. And add some variables necessary for initiation.
Open file openglestestAppView.cpp, add a function void CopenglestestAppView::initGL().
Open file openglestestAppView.h, add a draw function and a callback function to draw the scene.
Then open the file openglestestAppView.cpp to implement the function drawScence() and DrawCallBack( TAny* aInstance ).
Open the function void CopenglestestAppView::SizeChanged() and void CopenglestestAppView::Draw(const TRect& /*aRect*/) const, comment out the draw code.
Open the function ConstructL(const TRect& aRect) and add some initial code (set the window to full screen, ...)
Open the destructor function ~CopenglestestAppView() and add some code to free the resource has been used.
OK, here is what the above are for:
or
There are some things to notice here: you have to write ";" after any operation and you can assign seceral variables in the same declaration (like the "float z,q,l" above).
We can also consider the boolean operators here:
Arrays are characterised by the index(es) of their elements. The index is the number of the element in that sequence, counting from 0. For example, in 1,3,5,6, the index of the element "5" is 2. The elements of a matrix have to indexes, which are coordinates given by the line and column number. For example: in
And there you have it, a walkthrough of the basic C++ elements. C++ codes used for developing mobile phone applications are a bit different, but work on the same principles described here.
- Modify the mmp file
That's all to use STL for Symbian C++
(The section below is copied from the help file of Nokia (The Standard C++ API Reference))
In symbian we have two types of libraries in terms of there linkage that is static interface dll and polymorphic dll. Many times application statically linked with many dll which may or may not be required depending upon the different use cases but all get loaded into memory which can reduce the application performance because static dependency of the application or server can have large impact on the free RAM. Static dependences can be minimized by splitting the logical functionalities into dynamic loadable components.
void RuntimeLoadDllL()
{
RFs fileSession;
User::LeaveIfError(fileSession.Connect());
TFileName dllName;
If ( SomeCondition )
{
.......
dllName = KDllNameOne;
}
else
{
.........
dllName = KDllNameTwo;
}
LoadDllL(dllName);
.....................
fileSession.Close();
}
void LoadDllL(const TFileName& aLibName)
{
RLibrary lib;
User::LeaveIfErrorlib.Load(aLibName));
.....
}
ECOM also provide a standard mechanism to load specific implementation at run time.
The usage of TRAPs should be as minimal as possible because TRAPs consume more resources than one might assume because it result in kernel executive calls to TTrap::Trap() and TTrap::UnTrap() in addition a struct is allocated at runtime to hold the current contents of the stack in order to return to that state if unwinding proves necessary in the event of leave. If your class is contains more than 6 TRAPs it means your class is not designed properly and we need to redesign it. If possible move TRAPs to the higher levels in function call categories.
Immediately connecting to the servers when the application starts up might not be advisable as connection to the transient server because it can cause chain reaction, where multiple server dependent components are loaded into RAM.
Use the compresstarget statement in the application's MMP file to specify that the target executable should be compressed, by doing this the code and the data sections of the executable are compressed with the Huffman+LZ77 algorithm.
Always specify a drive letter in the file paths, if you know that. if the drive letter is not specified, all available drives are searched in the standard symbian OS order, if your file is present in Z: than you are in great loss because z: is always searched last.
In S60 application the default stack size for thread is only 8kB, so only small object should be created on the stack and removed from the stack as early as possible. We should always pass parameters by reference rather than by value, except for basic types, creation of large objects and array should be done on the heap and always try to minimize the lifetime of auto variable as much as possible.
If we are declaring default parameter for function argument, additional code is generated by the compiler which can be a overhead. If default value is being used often, then it’s worth overloading the method and not providing the default parameter.
void MyFunction( TInt aCounterVal, TInt aParamLength = 2);
Can be written as :
void MyFunction( TInt aCounterVa );
void MyFunction( TInt aCounterVal, TInt aParamLength );
Avoid passing large object by value always prefer reference or pointer. Unless a NULL value is acceptable, a reference is preferable because it is more preferable and simpler to use. Reference cannot be reassigned, if you need to point first an object and later other prefer pointer. References is always preferable than pointer because its more efficient due to the fact that the complier has to preserve the value of the null pointer through all conversions that is compiler has to test for the NULL value, thus creating further instructions.
Inline keyword doesn’t force compiler to do so, it’s completely on the compiler how should he behave. Avoid use of inline if your function is heavy that is if it contains more than 3-4 statements. Inline methods are advisable when there is some getter or setter methods are there, if we are using trivial constructor for T classes and when we are using thin templates.
Before writing any code we should always think about disk space. Especially when we are writing something on MMC or memory card. . RFs provide Volume() API to handle this issue.
TVolumeInfo volumeInfo;
....................
TInt err=ifsSession.Volume(volumeInfo, EDriveZ);
if (err != KErrNone)
{
........................
User::LeaveIfError(fsSession.DriveToChar(EDriveZ,driveLetter);
......................
}
Descriptors are designed to be efficient but it completely depends upon there usage. If possible try to minimize use T class descriptors like TPtrC, TPtr, TBuf or TBufC since these are created on the stack (rarely on heap) and should only be used for small strings basically maximum 16 characters are recommended. Always prefer TPtr or TPtrC over TBuf or TBufC. Try to use RBuf in place of HBufC because it makes the code easier to read & understand and hence to maintain and it help us to it reduces the possibility of errors and finally object code is slightly smaller also.
Usage of array is completely depends on the requirements. If array size is known at compile time only then TFixedArray can be used. However most likely that a dymanic array is required ,we can go for CArray or pointer arrays like CArrayPtrFlat, CArrayPtrSeg and RPointerArray. In general we always prefer RArray or RPointerArray over CArray or CArrayPtr because of their greater efficiency, flexibility and ease of use,RArrays are recommended over CArray types.
Normally applications have many releases and newer release can have lines of code (methods/data memebers) which was part of old releases and not required any more in your latest release(s), normally we call it as dead-code.Always remove unwanted dead code beacuse it can increase our ROM usage.
Here we only use the lastest version (at this time) of the tools.
If you are the C++ developer:
- Download and install the lastest version of Carbide.c++
- Download and install the lastest version of S60 Platform SDKs for Symbian OS, for C++
- Download and install the lastest version of Eclipse IDE
- Download and install the lastest version of S60 Platform SDK for Symbian OS, for Java
That's all you need. Let's begin to write the program.
Introduction to OpenGL ES
This article aims at providing an introduction to OpenGL ES for someone who is already experienced with desktop OpenGL, and shows how to use it with S60.
Introduction
OpenGL ES is a lightweight 2D/3D graphics library designed for embedded and mobile devices, based on the original OpenGL API. OpenGL ES version 1.0 is based on OpenGL 1.3, whereas OpenGL ES 1.1 is based on OpenGL 1.5. Currently, the Khronos Group is responsible for maintaining the OpenGL ES specifications.
OpenGL ES defines a concept named profile, that defines a subset of functionalities from the original OpenGL, plus features specific to OpenGL ES, such as extensions. Here they are:
- Common Profile: This profile is implemented in mobile devices such as cell phones and PDAs;
- Common-Lite Profile: This one is more restricted, and specifies the minimum required functionality to run 3D graphics applications. It is targeted at safety-critical devices, where reliability is a primary concern.
OpenGL ES also features an interface with the window system defined as EGL. The EGL API is common for all devices using OpenGL ES, but its implementation is hardware-dependent.
Required Files
SDKs from S60 2nd Edition FP2 onwards already have the required files to write OpenGL ES 1.0 applications. OpenGL ES 1.1 is available in the S60 3rd Edition FP1 SDK. The header files for OpenGL ES are:
#include #include
OpenGL ES is implemented as a DLL, so it is necessary to use these import library files (Carbide.vs):
libgles_cm.lib ws32.lib
The first file corresponds to OpenGL ES and EGL, Common Profile. The Common-Lite Profiles is not supported in S60 devices. The second file relates to the Symbian OS window server. For Carbide.c++, these files should have the .dso extension when building for the phone.
The following steps are required to start up OpenGL ES:
- Retrieve the default display device;
- Initialize OpenGL ES;
- Choose an OpenGL ES configuration;
- Create an OpenGL ES context;
- Create a drawing surface;
- Activate the OpenGL ES context.
As an example, we could use a class like the following one to accomplish these tasks:
#include #include #include "GLES/egl.h" #include "GLES/gl.h" class CGLRender: public CBase { public: // method to create an instance of this class static CGLRender* NewL (RWindow & aWindow); public: // destructor ~CGLRender (); // double buffering, more on this later void SwapBuffers (); private: // constructor CGLRender (RWindow& aWindow); // second part of the two-phase constructor, where // OpenGL ES is initialized void ConstructL(); private: RWindow iWindow; EGLDisplay iEglDisplay; EGLConfig iEglConfig; EGLContext iEglContext; EGLSurface iEglSurface; };
The iEglDisplay variable represents the device screen. The OpenGL ES configuration is stored in iEglConfig. The iEglContext variable represents the OpenGL ES context. Finally, iEglSurface represents the drawing surface.
Retrieving the device screen
iEglDisplay = eglGetDisplay (EGL_DEFAULT_DISPLAY); if (iEglDisplay == EGL_NO_DISPLAY) User::Panic (_L("Unable to find a suitable EGLDisplay"), 0);The EGL_DEFAULT_DISPLAY constant refers to the default device screen (in most cases there is only one). If the operation fails, the function returns EGL_NO_DISPLAY.
Initializing OpenGL ES
if (!eglInitialize (iEglDisplay, 0, 0) ) User::Panic (_L("Unable to initialize EGL"), 0);The last two parameters in this function correspond to the EGL implementation version. If you are not interested in it, just leave them as zeros. Otherwise, they are retrieved as this:
EGLint major, minor; eglInitialize (iEglDisplay, & major, &minor);For example, if the version is 1.0, major would be 1 and minor, 0.
Choosing an OpenGL ES configuration
Next, it is necessary to specify the minimum required configuration for the application.EGLint numConfigs; if (!eglChooseConfig (iEglDisplay, attribList, &iEglConfig, 1, &numConfigs) ) User::Panic (_L("Unable to choose EGL config"), 0);The attribList parameter represents an attribute list that the application requires. The function will return in the iEglConfig parameter a list of all the available configurations that match the attribute list. The size of this list is limited by the fourth parameter (in this case, we want only one configuration). The numConfigs parameter will inform the number of matching configurations, after the function returns. The attribute list defines a sequence of [attribute, value] pairs, as an array. The EGL specification defines constants for all supported attributes. For example, we will choose the color depth and z-buffer size:
// attribute list EGLint attribList [] = { EGL_BUFFER_SIZE, 0, // color depth EGL_DEPTH_SIZE, 15, // z-buffer EGL_NONE }; // here we use the same color depth as the device // iWindow is a RWindow object switch (iWindow.DisplayMode() ) { case EColor4K: attribList [1] = 12; break; case EColor64K: attribList [1] = 16; break; case EColor16M: attribList [1] = 24; break; default: attribList [1] = 32; }The list should end with the EGL_NONE constant.
Creating the OpenGL ES context
iEglContext = eglCreateContext (iEglDisplay, iEglConfig, EGL_NO_CONTEXT, 0); if (iEglContext == 0) User::Panic (_L("Unable to create EGL context"), 0);The third parameter indicates a context to share texture objects. Here, we use EGL_NO_CONTEXT stating that there is no such context. The last parameter represents an attribute list to be mapped to the new context, and in this case there is no such list.
Activating the context
For OpenGL ES commands to take effect, it is necessary to activate the context, making it current. In OpenGL ES, only one context can be current at a time.eglMakeCurrent (iEglDisplay, iEglSurface, iEglSurface, iEglContext);
Shuting down OpenGL ES
After using OpenGL ES, it is necessary to release all resources. Remember, this is very important!CGLRender::~CGLRender() { eglMakeCurrent (iEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroySurface (iEglDisplay, iEglSurface); eglDestroyContext (iEglDisplay, iEglContext); eglTerminate (iEglDisplay); }The first line deactivates the current context. Then, the surface and the context are destroyed. The last line finishes OpenGL ES.
By default, OpenGL ES uses double buffering. Here is how to perform this:
void CGLRender::SwapBuffers () { eglSwapBuffers (iEglDisplay, iEglSurface); }
Due to limitations of embedded devices, OpenGL ES does not include many redundant operations from OpenGL. For example, it is not possible to use immediate mode for specifying geometry in OpenGL ES. Hence, code like this one is not valid in OpenGL ES:
glBegin (GL_TRIANGLES); glVertex3f (0,1,0); glVertex3f (-1,0,0); glVertex3f (1,0,0); glEnd();
OpenGL ES renders all geometry from vertex arrays. So, this is the way to render a triangle in OpenGL ES, for example:
const GLbyte KVertices []= { 0,1,0, -1,0,0, 1,0,0 }; ... glEnableClientState (GL_VERTEX_ARRAY); glVertexPointer (3, GL_BYTE , 0, KVertices); glDrawArrays (GL_TRIANGLES, 0, 3);
As many devices do not have a FPU (floating point unit), OpenGL ES profiles define functions that accept fixed-point values. Fixed-point math is a technique to encode floating point numbers using only integers. When using fixed-point numbers, a integer is divided in two parts: a bit range is used for storing the integer part, and the remaining bit range is used to store the real part. OpenGL ES works with 16:16 fixed-point numbers, which means that it uses 16 bits to represent the integer part and other 16 to represent the fractional part. More information can be found here.
An example of using the translation function as fixed-point:
glTranslatex (20 << 16, 0, 0, 1 << 16); // same as // glTranslatef (20.0f, 0.0f, 0.0f, 1.0f);
The functions that accept fixed-point parameters have a 'x' suffix. Additionally, OpenGL ES introduces the GLfixed type to represent fixed-point numbers. Some other differences worth to mention:
- Almost all functions that do the same thing, but have different signatures (like glVertex3{fsidv}) do not exist in OpenGL ES. However, some of them like glColor4{fx} are there;
- OpenGL ES supports only the RGBA color mode (you do not have yo choose it);
- OpenGL ES does not render polygons as wireframe or points (only solid);
- It is not possible to query dynamic states in OpenGL ES 1.0 (for example, the current color);
- There is no GLU (OpenGL Utility Library). However, it is possible to find on the internet implementations of GLU functions, suitable for OpenGL ES;
- The GL_QUADS, GL_QUAD_STRIP, and GL_POLYGON primitives are not supported.
- The eglSwapBuffers function should not be called inside the Draw method of the View class. The actual rendering should be performed by another method. A timer class or active object should call this method, possibly in a callback function;
- It is advisable to use fullscreen mode for OpenGL ES applications. Sometimes, when not using it, the application may end up updating just half of it (on the phone). An application can set fullscreen mode by using the ApplicationRect() method from the application UI when creating the view;
- Specify geometry using fixed-point numbers, because many devices lack FPUs;
- It is important to avoid many state changes. For example, mixing texture-mapped polygons with ones not using textures decreases performance. In this case, a better solution is to create two groups of polygons and render them separately;
- Disable perspective correction for distant objects (because the effect will not be very noticeable);
- Disable or reduce effects that are too far to be noticeable;
- Lighting is cool, but it can increase processing requirements, so take care;
- If possible, try to group several images into one texture, so texture changes are minimized;
- If possible, try to submit many polygons in a single call, rather than doing many calls with few polygons.
#include "CGLRender.h" //_____________________________________________________________________________ CGLRender::CGLRender (RWindow & aWindow) : iWindow (aWindow) {} //_____________________________________________________________________________ CGLRender::~CGLRender() { eglMakeCurrent (iEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroySurface (iEglDisplay, iEglSurface); eglDestroyContext (iEglDisplay, iEglContext); eglTerminate (iEglDisplay); } //_____________________________________________________________________________ CGLRender* CGLRender::NewL (RWindow & aWindow) { CGLRender* instance = new (ELeave) CGLRender (aWindow); CleanupStack::PushL (instance); instance->ConstructL(); CleanupStack::Pop(); return instance; } //_____________________________________________________________________________ void CGLRender::ConstructL() { // attribute list EGLint attribList [] = { EGL_BUFFER_SIZE, 0, EGL_DEPTH_SIZE, 15, EGL_NONE }; // get device color depth switch (iWindow.DisplayMode() ) { case EColor4K: attribList [1] = 12; break; case EColor64K: attribList [1] = 16; break; case EColor16M: attribList [1] = 24; break; default: attribList [1] = 32; } // step 1 iEglDisplay = eglGetDisplay (EGL_DEFAULT_DISPLAY); if (iEglDisplay == EGL_NO_DISPLAY) User::Panic (_L("Unable to find a suitable EGLDisplay"), 0); // step 2 if (!eglInitialize (iEglDisplay, 0, 0) ) User::Panic (_L("Unable to initialize EGL"), 0); // step 3 EGLint numConfigs;
if (!eglChooseConfig (iEglDisplay, attribList, &iEglConfig, 1, &numConfigs) ) User::Panic (_L("Unable to choose EGL config"), 0); // step 4 iEglContext = eglCreateContext (iEglDisplay, iEglConfig, EGL_NO_CONTEXT, 0); if (iEglContext == 0) User::Panic (_L("Unable to create EGL context"), 0); // step 5 iEglSurface = eglCreateWindowSurface (iEglDisplay, iEglConfig, &iWindow, 0); if (iEglSurface == NULL) User::Panic (_L("Unable to create EGL surface"), 0); // step 6 eglMakeCurrent (iEglDisplay, iEglSurface, iEglSurface, iEglContext); } //_____________________________________________________________________________ void CGLRender::EnforceContext () { eglMakeCurrent (iEglDisplay, iEglSurface, iEglSurface, iEglContext); } //_____________________________________________________________________________ void CGLRender::SwapBuffers () { eglSwapBuffers (iEglDisplay, iEglSurface); }
Introduction to OpenGL ES - In practice
- Start Carbie C/C++
- Create new Symbian OS project
- Click on the button "Next", type the project name "openGLEStest" and then press button "Finish"
- Initiate the OpenGL ES (See part 1 for more detail)
- Implement the initial code
- Draw the scene
- Clean up resource
- Run the project
Open file openglestestAppView.h, add a function to initiate the OpenGL ES. And add some variables necessary for initiation.
Open file openglestestAppView.cpp, add a function void CopenglestestAppView::initGL().
Open file openglestestAppView.h, add a draw function and a callback function to draw the scene.
Then open the file openglestestAppView.cpp to implement the function drawScence() and DrawCallBack( TAny* aInstance ).
Open the function void CopenglestestAppView::SizeChanged() and void CopenglestestAppView::Draw(const TRect& /*aRect*/) const, comment out the draw code.
Open the function ConstructL(const TRect& aRect) and add some initial code (set the window to full screen, ...)
Open the destructor function ~CopenglestestAppView() and add some code to free the resource has been used.
How do I start programming for Symbian OS?
0.5: You already know some C++ basics, right?
- This basically assumes that you know the difference between an IDE, a compiler and a linker and you do not assume that there is only one IDE and one tool chain that can ever be used. You should also know that C++ is not [only] STL and that multi-threading is not the solution for all problems. ;)
- How many mistakes can you find in the C++ basics article?
1: Do you have a smartphone to test your application on?
- If not … no problem, skip to Section 3 below and select one of the SDKs, e.g the latest. Please note that usually the complexity of the SDK increases with each new release and that the pool of information that can help you solve the problems will be more limited for the new SDKs.
- Installing a SDK will allow you to test your created applications in a simulated S60 environment.
2: Read device's specifications on Forum Nokia's Device Specifications page.
-
- If the document says "Developer Platform: S60 3rd Edition" then the SDK for that device is S60 3rd SDK MR (an improved version of the S60 3rd SDK).
- If the document says "Developer Platform: S60 3rd Edition, Feature Pack 1" then the SDK for that device is S60 3rd SDK FP1.
- If you have two devices, one based on "S60 3rd Edition" while the other one on "S60 3rd Edition, Feature Pack 1" the recommended SDK is S60 3rd SDK MR, applications build with it would work on both devices (backward compatibility).
- If you have a device based on "S60 2nd Edition, Feature Pack X" and one based on "S60 3rd Edition, Feature Pack X" you cannot use one SDK for both devices due to binary and source compatibility breaks.
-
- More details about the S60 Platform, the existing versions and the differences between them is to be found here
3: Open the SDK download page
- ...but DO NOT download the SDK yet!
- Locate the "Release notes" section, download and read the release notes corresponding to your choice of the SDK. Please note that for one SDK release there might be different versions of it, providing support for various toolsets. Read them all before making a decision.
- From this document you will learn:
-
- if you need to have installed some 3rd party software like ActivePerl and Java Runtime Environment (JRE).
- which are the supported compilers (some freeware others under license)
- which are the supported IDEs (some freeware others under license)
- known issues (e.g. installation problems and known bugs or limitations)
-
-
- Note: At the time of writing this article the tools and SDKs provided by Nokia are not intended to be used on Microsoft's Windows Vista (tm) operating system. Unofficial support from the developer community can be found in this wiki in pages like: Moving to Windows Vista.
- Once you know which configuration suits you best ...
4: Download the SDK, tools and IDE of your choice.
- Install the packages (pre-requisites first, then the IDE and finally the SDK(s)). Install the tools in the suggested default location unless you are confident that you can handle some configuration tweaks.
- Make sure that each individual tool works( e.g. by calling it from the command line with the "-version" option) and that you have the minimum required version. (Not all these tools will be in your system, make a note of what you find and re-evaluate the situation after completing step 5 below.)
C:\>perl -version This is perl, v5.6.1 built for MSWin32-x86-multi-thread (with 1 registered patch, see perl -V for more detail) Copyright 1987-2001, Larry Wall Binary build 638 provided by ActiveState Corp. http://www.ActiveState.com ActiveState is a division of Sophos. Built Apr 13 2004 19:24:21 ... snip ...
- Note: Use the ActivePerl version recommended by the SDK release notes or the one recommended by Carbide. The latest ActivePerl releases are not compatible with the SDKs.
C:\>java -version java version "1.5.0_05" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_05-b05) Java HotSpot(TM) Client VM (build 1.5.0_05-b05, mixed mode, sharing)
- Note: The SDKs may not work by default with the latest JRE releases (e.g. JRE 6.0, aka JRE 1.6). Whether you have problems with it or just as a preventive measure you may want to have a look at the following discussion board thread: Cannot start ECMT Manager
C:\>mwccsym2.exe -version Nokia Codewarrior C/C++ Compiler for Windows/x86. Copyright (c) 2005, Nokia Corporation All rights reserved. Version 3.2.3 build 446 (Build 446) Runtime Built: Aug 15 2005 08:07:54
- Note: The Nokia Compiler is not included in the S60 SDK but it ships with Carbide.c++ and Carbide.vs. Also, even if the compiler is installed it may not be enabled by default for command line use. Please consult the Carbide documentation to learn about how to enable command line builds for the WINSCW platform.
C:\>arm-none-symbianelf-gcc.exe -v Reading specs from ... snip ... Configured with: ... snip ... Thread model: single gcc version 3.4.3 (release) (CodeSourcery ARM Q1C 2005)
- Note: This compiler is shipping with the SDK. If not found in your system it must be because you have not accepted the prompt to install it as part of the SDK install process.
C:\>armcc ARM/Thumb C/C++ Compiler, RVCT2.2 [Build 503] Usage: armcc [options] file1 file2 ... filen Main options: ... snip ...
- Note: The RVCT compiler is not provided with the SDK. This command line test would only work if you have installed the compiler on your PC and if you have a valid license for it.
5: Read the SDK documentation until you're confident that you understand:
-
- what Symbian OS is and how is it different from the other OS you have been programming for
- what programming for a mobile device means (constraints and opportunities)
- the structure of the OS, the main paradigms
- coding conventions
- system errors / panics / leaves and how to handle or log them
- differences between the device itself and SDK's emulator
- build system and tools, most importantly understand the "devices" tool
-
6: What's the hurry? … Go back to Section 5!
7: Start the emulator from Windows' start menu.
- Play with it, learn what applications are available, how to navigate in it and what "hidden" options are there in the menu. Almost everything is documented in SDK's help.
8: If you're here then you are ready to build your first application:
- Open a Windows command interpreter window and make the current working directory %EPOCROOT%S60Ex (Series60Ex on some SDKs).
- If you do not know what EPOCROOT is go back to §5.
-
- Go further in the directory structure by choosing one of the examples available there (e.g. one of the HelloWorld* versions). Once decided the example go deeper into its group folder.
- At the command prompt type the following command sequence:
…\group> bldmake bldfiles …\group> abld makefile all …\group> abld resource …\group> abld build winscw udeb …\group> epoc
- Needless to say that should you see any error message during the execution of any of the commands you must stop, evaluate the message, read the documentation and if needed take actions towards fixing those errors. Error messages can only be ignored once you know that they are not affecting your current build. If all goes well the last command will start the emulator and you will be able to test your first application … well your first application build anyway.
…\group> abld build gcce urel …\group> makesis ..\sis\helloworld.pkg
- The last two commands will build the application for the device target (go back to §5 to find out which is the supported target for your SDK & tools configuration) and then build an installation kit (sis file). You may need to sign it before transferring it on device. Install the application on device and have fun testing it.
9: Want to start coding now ? Hold your horses! … :)
- First you need to make sure that you fully understand that hello world (or whatever choice you had) example. Open the project directory in a file browser and analyze its content and structure. Don't go further until you fully understand the role of each file in that directory.
10: From now on you're on your own ...
... but the Symbian developer community will be happy to help you.
- See the Forum Nokia Developer Discussion boards Carbide.c++ and CodeWarrior Tools
- If you run into problems read the documentation. If you need more documentation visit www.forum.nokia.comand/or the Symbian Developer Network
- Before asking for help read … read … read. Don't miss these posts and definitely don't miss our technical library. You can also find a range of books and free booklets by Symbian Press here. If you are an absolute beginner, or just new to C++ on Symbian OS, the new version of Steve Babin's Developing Software for Symbian OS is a great book to help you get started.
- Use the forums to discuss your problems but make sure you are doing it "the smart way".
Source: http://wiki.forum.nokia.com/index.php/How_do_I_start_programming_for_Symbian_OS%3F
Basic C++ Elements
This article aims to provide information about the most basic C++ operations. It is intended for beginners in the C++ programming language.
OverviewC++ is a general-purpose programming language that supports procedural programming, object-oriented programming and generic programming. It was developed by Dr. Bjarne Stroustrup in 1979 at Bell Labs as an enhancement to the C programming language. C++ programs range from small applications that calculate the sum of 2 numbers to complex programs used in supercomputers.
Basically, C++ is very similar to Python and other programming languages. The environment consists of an editor (where you write the code) and a compiler (the code interpreter; simply, the part of the program that executes the instructions you've written). C++ is case sensitive, so "nokia" is not the same as "Nokia". C++ files are saved with a .cpp extension. Also, when you compile a program, an .exe file is created so you can use the program without having to open the editor and compile it every time.
There are a few free, open-source versions of C++ on the Internet, and also comercial versions, which provide slightly better features. The C++ compiler used for this tutorial is Dev-C++. After you download it, install it as you would any normal program. When you first run it, you are asked for some configuration options. The best ones are already selected, so just click "Next". After it is all set up, to open a new page, go to File->New->Source File (adapt these instructions to the C++ program you are using). After you've written an program, press F9 to save and compile it.
In C++, programs usually have the structure of a procedure (you'll understand better after you read the rest of this article). Here is an essential general structure of a C++ application, with explanation below:
//this is an example #include #include //etc. using namespace std; int main() { operation1; operation2; //etc. }
OK, here is what the above are for:
- "//this is an example" is a comment. Comments are words used to describe operations and thus make the code easier to understand. They are optional, but recommended.
- "#include": # is a key character (this means it has to be there), "include" tells the compiler to include the module named "module1" (modules are, roughly said, collections of information and functions that C++ uses in order to perform operations)
- "using namespace std;" this also has to be here, though it is not its only form
- "int main()" "int" means that the function named "main" will return an integer result. We'll discuss this when we get to variables.
- "{" marks the beginning of a group of operations, in this case the body of the program
- "operation1" is an operation that the program has to perform
- "}" marks the end of a group of operations, in this case the body of the program
A variable is a "container" that is used to store a value. For example:
a=7;
stores the value 7 in the variable named a.
Classification
In C++, you may use the following types of variables:
- int - an integer number, roughly between -2100000000 and 2100000000
- long - an "extended" version of int; rarely used
- float - a number with decimals
- double - an "extended" version of float; a little less stable
- bool - a logical variable (can be TRUE or FALSE)
- char - a character
- string - a sequence (may also be called array) of characters
Assignment and examples
There are 2 ways to assign a value to a variable:
int x=22434; long z=34; float a=1.2; double y=3.4; bool q=true; char c="p"; string s="ala";
or
int x; x=4; float z,q,l; z=3; q=-1.7; //etc.
There are some things to notice here: you have to write ";" after any operation and you can assign seceral variables in the same declaration (like the "float z,q,l" above).
Operators
Like in mathematics, you can compare values. A comparison can be true or false:
2<5>=34 is false
Operators are useful for conditions (see later in this article). The operators used in C++ are: < (smaller), > (greater), <= (smaller or equal), == (equal), >= (greater or equal), != (different) Here is an example
a=4; b=18; x<=y x!=y x>=y //return true x>y x==y //return false
We can also consider the boolean operators here:
- "and" - returns true if all values are true, false otherwise
- "or" - returns true if at least one value is true, false otherwise
- "not" - returns true if the value is false and vice-versa
Examples:
(2<5)>1) returns true (3<1)>3456) returns false not(4<5)>
Operations
Operations are used to modify variables. Here is a list of the most commonly used operations:
- "+" - adds two values
- "-" - subtracts the value of the second from that of the first
- "*" - multiplies two values
- "/" - divides a value to another and returns the result without the modulus (i.e. 5%2=2)
- "%" - the modulus (the number you get after dividing a number to another number, i.e. 5%2=1)
Incrementing (increasing a value by 1) and decrementing (decreasing a value by 1) are also operations. This is how it is done:
int i=0; for each operation below, the initial value of i is 0 i=i+1 is the same as i+=1 and means that i is given its current value +1. i++ means i is used with its current value and then increased by 1 ++i means i is increased by 1 and its new value is used The same goes for "-" instead of "+", the difference being that 1, or whatever quantity you are adding, is subtracted.
Note that i=i*3, i*=2, i/=5 etc are also valid operations.
In order to check if a condition is met we use the "if" conditional:
if(condition) { operation1; operation2; ... } else { operationa; operationb; ... }
This is what happens: if the "condition" is true "operation1", "operation2"... are executed. If the "condition" is false "operationa", "operationb"... are executed. In the following example,
#include using namespace std; int main() { int x=2; if((x==1) or (x<45)) { x=7; x=x+1; } else x--; }
the final value of x should be 8.
If we need to execute a series of instructions a certain number of times, or until a condition is met, we use the "while" and "for" loops.
"While" reapeats the operations until the condition set by the programmer is met. Remember to modify a variable once the desired result is achieved in order to avoid infinite loops.
while(conditions) { op1; op2;... }
Here is an example:
int x,i; x=5, i=1; while(i<=x) { cout << "that's one loop"; i++; }
This should print the message "that's one loop" 5 times.
"For" does the instructions as many times as the range of the variable says. The variable is automatically incremented/decremented, so there is no risk of infinite loop.
int variable; for(variable=startingValue; variable) { op1; op2;... }
Example:
int i; for(i=1; i<=5; i++) cout << "that's one loop";
Arrays are sequences of values. The most common types of arrays are uni-dimensional (vertice) and bi-dimensional (matrix). Examples:
- a vertice:
1,2,3,4
- a matrix:
1 2 3 4 5 6 7 8 9
Arrays are characterised by the index(es) of their elements. The index is the number of the element in that sequence, counting from 0. For example, in 1,3,5,6, the index of the element "5" is 2. The elements of a matrix have to indexes, which are coordinates given by the line and column number. For example: in
0 -3 5 6 2 5 5 67
the indexes of the element "-3" are 1 (because it is on the first line) and 2 (because it is on the second column)
Here is how to declare and read arrays:
- Vertice:
int vert[noOfElements]; int i; for(i=1; i<=noOfElements; i++) cin >> vert[i];
Where noOfElements is the maximum number of elements the vertice will ever have in our program. "cin" reads the value from the user.
- Matrix
int mat[noOfL][noOfC]; int i,j; for(i=1; i<=noOfL; i++) for(j=1; j<=noOfC; j++) cin >> mat[i][j];
Where "noOfL" is the number of lines and "noOfC" is the number of columns.
Note that arrays can be of many kinds depending on the values they store. They can have integer, decimal, character and string elements. A string is an array all on its own. Its elements are its characters. For example:
s="yeah"; s[0] will return "y", s[3] will return "h" etc.
Functions are used to return the result of a series of operations that you don't have to write. In other words, instead of writing a series of operations, you just call a function that has them written in it.
Let's see an example. Say we want to make the function "factorial", which, for a given integer number x, returns 1*2*3*...*x.
#include using namespace std; //functions are created before they are used, in this case before the main program int factorial(int x) //between the brackets is the list of variables the function uses { int i, f; f=1; for(i=1; i<=x; i++) f=f*i; return f; } int main() { int x=5; cout << style="color: rgb(0, 0, 0);">(x); //simply call the function and have it calculate for the value x system("pause"); //makes the computer wait for a keypress instead of closing return 0; //the window in a second }
This should display 120. Functions are very useful in the development of large applications because they make it so you don't have to write all that code each time, but instead just call them.
Here we are, ready to make our first C++ program. Let's make the classic "Hello, world!":
1.Open your C++ editor (in this case Dev-C++)
2.Open a new file (File->New->Source File)
3.Write the following code:
#include using namespace std; int main() { cout << "Hello, world!"; system("pause"); return 0; }
4.Compile the program (F9)
5.If all goes well you should see the message.
Here's a program that calculates the sum of n given numbers:
#include using namespace std; int main() { int n, s=0, i; cin >> n; int v[n]; for(i=1; i<=n; i++) { cin >> v[i]; s=s+v[i]; } cout << style="color: rgb(0, 0, 221);">system("pause"); }
And there you have it, a walkthrough of the basic C++ elements. C++ codes used for developing mobile phone applications are a bit different, but work on the same principles described here.
Recommended reading:
- The great articles on this Wiki
Source: http://wiki.forum.nokia.com/index.php/Basic_C%2B%2B_Elements
STL for Symbian C++
Download OpenC/C++ Plugin
- Install it- Modify the mmp file
That's all to use STL for Symbian C++
(The section below is copied from the help file of Nokia (The Standard C++ API Reference))
Changes to the MMP file
Add needed libraries used by the MMP file structure:If developers want to use any of the Standard C++ library, they need to link to the corresponding library in the MMP file using the LIBRARY keyword.
If the application has main() as the entry point, the library libcrt0.lib must be specified as the first library otherwise, it will result in linker errors. The user must link to the Symbian OS euser.dll. This is required since the static library uses some of the services of the Symbian OS such as creating cleanup stack, and having a top level TRAP. All these details are hidden from the developer. The developer will write the application as if it were for the UNIX environment.
The libcrt0.lib library is required if we are not going to write E32Main within our application (EXE). This static library has an implementation of E32Main within which it calls the library initialization method followed by calling main written by the developer. This static library also gets command-line arguments and passes the same to main.
If the application has E32Main() as an entry point, there is no need to link to libcrt0.lib like in the example below.
Linking of libstdcpp
The following snippet shows how to perform the linking to libstdcpp on an emulator:
Add the below option and macro in the MMP file
If the application has main() as the entry point, the library libcrt0.lib must be specified as the first library otherwise, it will result in linker errors. The user must link to the Symbian OS euser.dll. This is required since the static library uses some of the services of the Symbian OS such as creating cleanup stack, and having a top level TRAP. All these details are hidden from the developer. The developer will write the application as if it were for the UNIX environment.
STATICLIBRARY libcrt0.lib LIBRARY libc.lib LIBRARY euser.lib // Needed in order to use Symbian services
If the application has E32Main() as an entry point, there is no need to link to libcrt0.lib like in the example below.
LIBRARY libc.lib LIBRARY euser.libAdd needed include paths
SYSTEMINCLUDE \epoc32\include\stdapis SYSTEMINCLUDE \epoc32\include\stdapis\sys SYSTEMINCLUDE \epoc32\include\stdapis\stlport
The following snippet shows how to perform the linking to libstdcpp on an emulator:
#ifdef EPOC32 LIBRARY libstdcpp.lib #else FIRSTLIB ../udeb/libstdcpp.lib STATICLIBRARY eexe.lib #endif
//This is required even if the wchar type is not used. OPTION CW -wchar_t on MACRO _WCHAR_T_DECLARED
NOTE: Standard C++ applications may require more stack space. The recommended stack size is 10K. To set the stack size to 10K add:
EPOCSTACKSIZE 0x10000in the MMP file.
Example using main()
A simple example using main() as an entry point is described below. The example writes a text to a console.- Modify the MMP file as mentioned before.
- Do usual C++ style coding.
// Include Files #include #include // This is a GCCE toolchain workaround needed when compiling with GCCE // and using main() entry point #ifdef __GCCE__ #include #endif using namespace std; class myclass { public: void show(){cout<<"Hello World\n"; } } ; int main() { myclass obj; obj.show(); cout<<"Press a character to exit!"; int c = getchar(); return 0; }
Performance Tuning of Code
If an application runs and performs all required task for the user or client, doesn’t means that it is complete in all the sense. Using performance analysis tools like profiler we can find out how our application is performing internally means in terms of speed, memory usage and power utilization.
There are lots of issues which can reduce performance of your application; below I am listing some of them
Issue 1: Dll Usage Optimization:
In symbian we have two types of libraries in terms of there linkage that is static interface dll and polymorphic dll. Many times application statically linked with many dll which may or may not be required depending upon the different use cases but all get loaded into memory which can reduce the application performance because static dependency of the application or server can have large impact on the free RAM. Static dependences can be minimized by splitting the logical functionalities into dynamic loadable components.
void RuntimeLoadDllL()
{
RFs fileSession;
User::LeaveIfError(fileSession.Connect());
TFileName dllName;
If ( SomeCondition )
{
.......
dllName = KDllNameOne;
}
else
{
.........
dllName = KDllNameTwo;
}
LoadDllL(dllName);
.....................
fileSession.Close();
}
void LoadDllL(const TFileName& aLibName)
{
RLibrary lib;
User::LeaveIfErrorlib.Load(aLibName));
.....
}
ECOM also provide a standard mechanism to load specific implementation at run time.
Issue 2: Check usage of TRAPs:
The usage of TRAPs should be as minimal as possible because TRAPs consume more resources than one might assume because it result in kernel executive calls to TTrap::Trap() and TTrap::UnTrap() in addition a struct is allocated at runtime to hold the current contents of the stack in order to return to that state if unwinding proves necessary in the event of leave. If your class is contains more than 6 TRAPs it means your class is not designed properly and we need to redesign it. If possible move TRAPs to the higher levels in function call categories.
Issue 3: Check Server Usage and Optimization
Immediately connecting to the servers when the application starts up might not be advisable as connection to the transient server because it can cause chain reaction, where multiple server dependent components are loaded into RAM.
If the server is rarely used, this approach can be seen as ineffective resource utilization. On-demand connection to transient is always advisable. Design should be in such a way that client API implementation should be kept separate from the actual server implementation in this way when the client links to the client API library, it will not load server dependencies automatically which should be happen after the server is really started.
While designing server always think about its usage and the various resources it needs at runtime. Suppose server provide three features like Read, Write and Notification and read/rite is not very frequent operation in such scenario its better to breakup into two servers one which will provide heavyweight functionality like read/rite/replace etc.. This will be loaded/unloaded depending upon client requirements and other into always up light weighted notified server.
Issue 4: Compressing the executable:
Use the compresstarget statement in the application's MMP file to specify that the target executable should be compressed, by doing this the code and the data sections of the executable are compressed with the Huffman+LZ77 algorithm.
It allows the stored executables to use less space on the file system. The executable loader decompressed the file when it is loaded. All executable should not be decompressed, only those who can take large amount of space are candidate for that.
Issue 5: File Scanning:
Always specify a drive letter in the file paths, if you know that. if the drive letter is not specified, all available drives are searched in the standard symbian OS order, if your file is present in Z: than you are in great loss because z: is always searched last.
Issue 6: Check Memory Usage Optimizations:
In S60 application the default stack size for thread is only 8kB, so only small object should be created on the stack and removed from the stack as early as possible. We should always pass parameters by reference rather than by value, except for basic types, creation of large objects and array should be done on the heap and always try to minimize the lifetime of auto variable as much as possible.
Issue 7: Check usage of default parameters
If we are declaring default parameter for function argument, additional code is generated by the compiler which can be a overhead. If default value is being used often, then it’s worth overloading the method and not providing the default parameter.
void MyFunction( TInt aCounterVal, TInt aParamLength = 2);
Can be written as :
void MyFunction( TInt aCounterVa );
void MyFunction( TInt aCounterVal, TInt aParamLength );
Issue 8: Check Usage of Reference and Pointer:
Avoid passing large object by value always prefer reference or pointer. Unless a NULL value is acceptable, a reference is preferable because it is more preferable and simpler to use. Reference cannot be reassigned, if you need to point first an object and later other prefer pointer. References is always preferable than pointer because its more efficient due to the fact that the complier has to preserve the value of the null pointer through all conversions that is compiler has to test for the NULL value, thus creating further instructions.
Issue 9: Check for inline keyword and Initialization list
Inline keyword doesn’t force compiler to do so, it’s completely on the compiler how should he behave. Avoid use of inline if your function is heavy that is if it contains more than 3-4 statements. Inline methods are advisable when there is some getter or setter methods are there, if we are using trivial constructor for T classes and when we are using thin templates.
There are some more issues related to inline methods like change in the implementation can break binary compatibility and if you have issues like limited memory resource than speed cost of a method call is preferable to the large body of inline code.
Using initialization list for constructors is more efficient than assignment within the constructor body because class member variables are automatically constructed using there default constructor prior to entry within the class constructor itself. However the constructor lists does not allow us to validation of variables and hence they are not always suitable.
Issue 10: Check disk space issue:
Before writing any code we should always think about disk space. Especially when we are writing something on MMC or memory card. . RFs provide Volume() API to handle this issue.
You can get volume information comprising all the information provided by DriveInfo, which additionally gives the volume name, its ID, its size and the amount of free space.
Use RFs::Volume() to get a TVolumeInfo objectTVolumeInfo volumeInfo;
....................
TInt err=ifsSession.Volume(volumeInfo, EDriveZ);
if (err != KErrNone)
{
........................
User::LeaveIfError(fsSession.DriveToChar(EDriveZ,driveLetter);
......................
}
Issue 11: Check usage of Descriptors:
Descriptors are designed to be efficient but it completely depends upon there usage. If possible try to minimize use T class descriptors like TPtrC, TPtr, TBuf or TBufC since these are created on the stack (rarely on heap) and should only be used for small strings basically maximum 16 characters are recommended. Always prefer TPtr or TPtrC over TBuf or TBufC. Try to use RBuf in place of HBufC because it makes the code easier to read & understand and hence to maintain and it help us to it reduces the possibility of errors and finally object code is slightly smaller also.
Avoid creation of multiple local instances of class objects like TFileName, TParse etc.., better to have a member variable.
Avoid usage of _L literal macros because of extra runtime overhead associated with construction of temporary descriptor which is the why they are deprecated in production code.
Issue 12: Check usage of Arrays:
Usage of array is completely depends on the requirements. If array size is known at compile time only then TFixedArray can be used. However most likely that a dymanic array is required ,we can go for CArray or pointer arrays like CArrayPtrFlat, CArrayPtrSeg and RPointerArray. In general we always prefer RArray or RPointerArray over CArray or CArrayPtr because of their greater efficiency, flexibility and ease of use,RArrays are recommended over CArray types.
CArray classes re-use the CBufBase framework to provide two alternative storage structures for the array data with very little CArray specific code. There are two assertions for every array access, one in the CArray code and one in the CBufBase code plus a number of TRAP harnesses are used to catch allocation failure. In addition, the CBufBase code operates in terms of byte buffers, and these appear in the API as descriptors. In the implementation, this requires a TPtr8 to be constructed, returned and inspected for every array access. This means that, although very powerful, the implementation of the CArray classes can add considerable overhead for simple vector-like flat arrays of fixed length objects.
Don’t feel that CArray is so bad because the main benefit from the CArray classes is their support for segmented storage and their ability to handle arrays of variable length objects. However, the latter is a rare requirement. and RArray stores objects in the array as word (4 byte) aligned quantities. This means that some member functions do not work when RArray is instantiated for classes of less than 4 bytes in size, or when the class's alignment requirement is not 4. Be aware that it is possible to get an unhandled exception on hardware that enforces strict alignment. This will affect methods like RArray constructor, Append(..), Insert(..) and [] operator.
Try to avoid RArray if you want to store "C" classes. You should use RPointerArray.
The reason is that RArray does not call copy-constructor of your class, but it will do a bit-wise copy that can be dangerous. Therefore RArray is useful for "T" classes and for "C" classes use RPointerArray. One more point that we can not ignore is RArrays impose a limitation of 640 bytes on the size of their elements. Usually this is not an issue, since most objects are likely to be smaller than this limit, and RPointerArray can be used to point to arbitrarily large objects but normally size of object is less than 640 bytes.
The reason is that RArray does not call copy-constructor of your class, but it will do a bit-wise copy that can be dangerous. Therefore RArray is useful for "T" classes and for "C" classes use RPointerArray. One more point that we can not ignore is RArrays impose a limitation of 640 bytes on the size of their elements. Usually this is not an issue, since most objects are likely to be smaller than this limit, and RPointerArray can be used to point to arbitrarily large objects but normally size of object is less than 640 bytes.
Based on runtime extension requirements we can go for Segmented or Flat type.
Iteration in the flat arrays is fast because of its contiguous nature where as segment
array expansion is fast because internally its implemented as linked list.
Iteration in the flat arrays is fast because of its contiguous nature where as segment
array expansion is fast because internally its implemented as linked list.
Issue 13: Presence of Dead-code :
Normally applications have many releases and newer release can have lines of code (methods/data memebers) which was part of old releases and not required any more in your latest release(s), normally we call it as dead-code.Always remove unwanted dead code beacuse it can increase our ROM usage.
Nice Tutorials About Symbian OS keep it up
ReplyDelete