Cross Platform Open Source Development

1

How to start working with us.

Geolance is a marketplace for remote freelancers who are looking for freelance work from clients around the world.

2

Create an account.

Simply sign up on our website and get started finding the perfect project or posting your own request!

3

Fill in the forms with information about you.

Let us know what type of professional you're looking for, your budget, deadline, and any other requirements you may have!

4

Choose a professional or post your own request.

Browse through our online directory of professionals and find someone who matches your needs perfectly, or post your own request if you don't see anything that fits!

Cross platforms software is a software program whose development and implementation can run on several computer platforms. Some cross-platform software is built separately from any platform and can also be run without special preparation and written in a translated language; it may be written to portable bytecode where interpreter packages or runtime packages have been shared or standard parts. Examples include cross platforms that can operate with Mac OS, XP OSX, or Windows. Cross-platform software runs on several platforms.

Looking for software that can run on multiple platforms? Look no further!

Geolance offers cross-platform software that is easy to use and can be implemented on various computer systems. You won't have to worry about compatibility issues – our software works with Mac OS, XP OSX, and Windows.

Cross-platform software is essential for businesses that want to operate efficiently on multiple systems. With Geolance, you can easily switch between platforms without losing productivity. Plus, our software is available at an affordable price.

Implementation of the software can run across platforms that are not compatible.

The cross platform app must be built separately for each platform to implement the cross-platform. The developer must also ensure that its modules work well with all standard operating systems. The advantage of cross-platform is that it reduces cross platform app development time and cost.

Before making a game or any other program run on several different operating systems like Windows, Windows CE, Mac OS, Android, etc., nowadays, programmers use cross-platform tools that break down the walls between these different environments. Nowadays, many software tools allow programmers to compile their code once and then run it on any device from desktops running Linux or Windows to mobile phones with iOS or Android as their operating system.

Making a game or any other program run on several different operating systems like Windows, Mac OS, Android, etc. But nowadays, many tools allow programmers to compile their code once and then run it on any device from desktops running Linux or Windows to mobile phones with iOS or Android as their operating system.

Cross-platform is a software developer term that refers to the ability for a computer program source code to be compiled or interpreted and run on more than one hardware platform or operating system family without porting the source code manually or having access to recompile the source. Cross-platform is limited to binary compatibility between different CPUs with incompatible instruction set architectures and applies even if the hardware is based on a similar or identical instruction set. Cross-platform programs are more portable than platform-specific software because programmers can use libraries and modules written for an abstract platform rather than actual computer hardware. There are several ways in which this happens:

Programs that use a cross-platform system library typically make standard calls into that library, resulting in each program having its copy of library routines at runtime; all but one of these copies (and thus most of the code executed by the program) will be identical even if compiled for different platforms. If necessary, some porting may still be required to adjust the data types used by routines when dealing with platform-dependent concepts such as file paths or user accounts.

Programs may be written in a cross-platform programming language, which will allow source code to be compiled into platform-specific executable code. The resulting process is sometimes called pseudo-cross-platform or source-based, and it often takes place in a pre-compilation stage such as a toolchain. Once a program has been written cross-platform (or at least the core part), the same compiler can produce new versions for many platforms with minimal effort. However, significant or complete rewriting may still be required if portability across more than one operating system family is needed. There are several ways in which this happens:

A program may access an emulation library that effectively provides the functionality of an emulator on platforms where such is not available. Another method of increasing the portability of a program is to use only cross-platform programming languages that can be converted to machine code with a single compilation processor that does not require compilation at all.

Programs designed for some embedded systems may deliberately omit facilities considered target-specific to reduce the size and complexity of the executable, leaving out features like floating-point arithmetic, file input/output beyond simple keyboard I/O, or user interface toolkits (in favour of highly efficient textual communication over an auxiliary serial link). A related concept is graceful degradation, i.e., avoiding problems by providing partial functionality when full functionality is not present; this practice applies mainly to user interfaces. For example, a cross-platform program may check which operating system is in use before opening a file using extensions specific to that operating system, rather than generating an error message when run on systems where these extensions are not available.

Most programmers, though, prefer C++ and *nix because of the availability of free compilers and development tools for almost any platform without license fees. Another advantage of developing on Unix and its derivatives is that most support some emulation environment that can emulate other hardware/OS configurations. This feature enables porting applications from one Unix variant to another with relative ease. The availability of so many Unix flavours causes no difficulty since cross-platform programs usually compile and execute unmodified on all common platforms.

The potential for portability problems in cross-platform development is more significant than in single-platform development because there are more ways in which different platforms may differ from each other.

Some of the most common areas where porting issues arise include

1) A program written using one kind of library or module may not link on another platform if that library uses symbols only available on the first platform (usually non-standard extensions). For example, a program written for Linux may need to be recompiled with an OpenVMS compatible compiler before it can run on OpenVMS; some programs use libraries specific to Unix or Windows NT but do not compile or run under any other environment. Also, APIs (such as the Win32 API) may be implemented by different DLLs on different platforms, making calls to them non-portable.

2) A program written using one kind of library or module may not link if it makes assumptions about the code organization in memory. For example, Linux systems can have executables greater than 2 GB in size, but other systems have a file size limit which is less than 4 GB.

3) Libraries and modules supplied with core system software may not always be available for all platforms. This means that alternative versions of these pieces of code must sometimes be used instead, which increases maintenance effort. One example is libraries using features specific to Windows NT that are unavailable on other platforms.

4) The behaviour of system calls and library routines may be different between different variants of the same operating system (which may be more or less strict about certain aspects of their specification). For example, some UNIX systems require that many system calls return EFAULT when given invalid addresses, while others return NULL instead. Also, POSIX does not specify whether functions like fopen() lock files by exclusive or shared access; some Unix flavours use one kind and some another. Other examples include: Windows NT's GetTickCount() is only guaranteed to be accurate up to approximately 49 days rather than a few hundred years as specified in the corresponding Portable Operating System Interface (POSIX) specification; and *nix systems vary in whether they provide the ability to lock files on network shares.

5) An application may use non-portable constructs in its source code, resulting in warnings or errors when compiling for another platform. This can be caused by: using compiler-dependent types such as size_t and uintptr_t; placing statements in the wrong part of a switch statement (e.g., putting a case label before all preceding case labels); incorrect usage of function calls such as snprintf(); and incorrect STL usage such as passing int values instead of size_t values to vector::push_back().

6) Libraries and modules supplied with system software may not always be available for all platforms, resulting in alternative versions, which increases maintenance effort.

7) The behaviour of system calls and library routines may be different between different variants of the same operating system (more or less strict about their specification). For example, some Unix systems require that many system calls return EFAULT when given invalid addresses, while others return NULL instead. Also, POSIX does not specify whether functions like fopen() lock files by exclusive or shared access; some Unix flavours use one kind and some another.

8) An application may use non-portable constructs in its source code, resulting in warnings or errors when compiling for another platform. This can be caused by using compiler-dependent types such as size_t and uintptr_t; placing statements in the wrong part of a switch statement (e.g., putting a case label before all preceding case labels); incorrect usage of function calls such as snprintf(); and incorrect STL usage such as passing int values instead of size_t values to vector::push_back().

9) The behaviour of system calls and library routines may differ between different variants of the same operating system (more or less strict about their specification). For example, some Unix systems require that many system calls return EFAULT when given invalid addresses, while others return NULL instead. Also, POSIX does not specify whether functions like fopen() lock files by exclusive or shared access; some Unix flavours use one kind and some another.

10) Portability problems can result from types defined by compiler-dependent headers. If a header file is not included in both the application and its libraries, then it will be impossible to compile or link against that library. For example, Gtk+ defines the gintptr type as a signed integer, but some compilers define it as an unsigned integer.

The best way to avoid these problems is code reuse, which means writing your software so you can reuse as much of it as possible on multiple platforms without change. You should always prefer standards-based interfaces over non-standardized interfaces (for example, POSIX versus Windows).

If you must develop platform-specific applications (and sometimes even if you don't), several techniques may help reduce portability: use only standard C++ features; avoid compiler- and OS-specific features; use STL containers and algorithms instead of homegrown code, and use preprocessor defines to separate platform-specific code from the rest.

Some portability problems can be challenging to detect without thorough testing. Many compatibility layers allow applications to run on different platforms, such as Capone, Compat++, Cygwin, Linux, Portable, Wabi, and Wine (formerly known as X11 Windows Emulator). These compatibility layers may not always fully emulate the target environment, and some app developers prefer writing platform-specific applications.

Combined with backward dependence in both Windows 10 APIs and earlier versions, even if an app is built with the latest supported Visual Studio on Windows 10, it will still use Win32 APIs instead of UWP ones.

"Microsoft recommends against building UWP mobile apps with any version of Visual Studio earlier than 2015 Update 3 or on Windows versions earlier than Windows 10."

As older applications are not published in the Store anymore, you don't need to worry about new APIs - just enjoy your old pieces of software running on newer versions of the operating system. But unfortunately, it's almost impossible for third-party developers who care about their programs being available as long as possible to leave some specific versions out of support. And Spectre mitigations may be one reason why - although there are no actual changes in the security model, applications using data from another process may not work correctly on Windows 8.1 and even more so on seven as it depends on several system-specific features that are either deprecated or completely removed starting with those versions.

Graphics subsystem compatibility issues include problems caused by different pixel formats, resolution changes without screen refresh (on battery power) and colour depth changes.

As long as you're developing a cross-platform application or a component for one, you should avoid processor-dependent code - do not use inline assembly whose behaviour is dependent on CPU type. Also, be careful when using dynamic memory allocation since some operating systems have different heap implementations than others. E.g., an object allocated using malloc() may have a different size depending on the OS version.

Not all differences are negative - macOS-only frameworks like SpriteKit (for game development) or Metal (for graphics acceleration) may be unavailable on Windows and Android. Still, they can benefit your software by making it more efficient.

11) Cross-platform applications often need to integrate with their environment, including knowing where they are installed, checking registry entries, using command-line arguments, etc. Some of these features cannot be supported cross-platform without significant engineering effort.

For instance, you should not hardcode paths for loading data files or images unless there's no other option. Also, consider how the application will find its configuration file(s). And don't use undocumented APIs even if they exist on all Windows versions you want to support - some of these APIs may change without any warning between different service packs or even editions of the system.

12) Cross-platform applications must not hardcode paths and other resources in the filesystem, registry, configuration files, etc. You should use environment variables instead.

If your application uses command-line arguments, you cannot pass them in a platform-independent way using argc and argv - use EnvDTE::CommandLineArgs objects instead (see the Microsoft Visual Studio documentation for details). To get the current working directory, you can use the DTE::ExecutableDir property or even simpler DTE::SolutionDir. And here's how to get a list of recent files:

13) Cross-platform applications should not depend on environment variables and other settings defined outside their scope.

Suppose your program takes the path to a resource as an argument. You cannot use the GetModuleFileName function or similar - instead, pass the actual filename (absolute or relative), and the operating system will resolve it at run time.

14) The application should not depend on properties such as current directory, working directory, locale information -- these must come from the environment in which the application runs.

There may be different ways to find out whether an application is running for the first time or it's being launched after an upgrade: in both cases, it can check if there is a file with its name in the appropriate directory and read its contents; in case of upgrades it can also check whether there's a newer version available.

15) The application should not depend on the current working directory or registry keys -- these must be retrievable from the environment in which the application runs.

16) The application should not depend on the computer's locale settings and should use Unicode throughout because Some languages require Unicode handling for correct operation (Arabic and Hebrew); Non-Unicode applications cannot run on Windows NT 4.0 or XP system when using non-Latin1 language packs;  There are some problems with console support in non-Unicode applications when switching locales.

Note that this does not apply to console/text-mode programs - they must use ANSI APIs everywhere. However, if you're creating a GUI application that uses Unicode internally, it is safe to assume that users will want to use their system language settings.

17) The application should be written in Unicode (UTF-16 or UTF-32) throughout - this allows it to run on any Windows locale and prevents problems with support for different locales.

18) If your program echoes input, ensure that it echoes all characters (including sensitive ones like passwords).

19) Cross-platform applications must not make assumptions about character data encoding passed as command-line arguments or read from configuration files.

20) To prevent spoofing attacks, cross-platform applications should not check the IP address of incoming connections but rather accept them if they come from pre-configured trusted hosts.

21) Use the DTE and DCE Application object models (see the DTE and DCE articles). They provide a consistent interface to access standard functionality such as clipboard, file system, registry, networking, etc. Both Windows and Mac OS X are also accessible via COM.

22) The application must open files for writing in binary mode (some C++ compilers do this automatically if you use ios::out | ios::binary, others require explicit initialization - see the documentation for your compiler).

23) Do not use unsupported or deprecated APIs. This includes obsolete COM interfaces like IBindStatusCallBack. Also, note that some methods and COM interfaces may be available only on specific OS versions - do not assume they will always be available.

24) If the application is a plugin for an existing IDE, it must follow the guidelines of its host. For example, Visual Studio plugins should not use GetCurrentProcessId as this function is intended for interactive UI programs only.

25) Cross-platform applications must not print to stdout/stderr by calling the C Run-Time library print/print functions because these work with ANSI strings terminated at each CR ('\r') character. In contrast, the Unicode text does not contain CRs (in Windows string literals, "n" prefixed to a string literal denotes non-text mode). Instead, use the C++ iostreams (e.g. std::cout << "Hello" << std::endl;) or unmanaged APIs like the Win32 WriteConsoleW function.

26) Do not use unsupported or deprecated APIs in statically linked libraries that your application references - this will cause portability problems for end-users as these APIs may be removed or changed without prior notice or deprecation period.

27) Use platform-specific API only if it provides additional functionality - otherwise, stick to cross-platform code as it is easier to write and maintain and requires less testing on various platforms. The list above includes many other miscellaneous tips; please check them out!

Singular features that you can't do without

1) If your application requires platform-specific functionality, write separate executables for each supported platform (i.e. do not make one executable with internal checks for the current OS version). Do not fail silently if the user runs your app on an unsupported platform - inform them about the disadvantages of using unsupported platforms and let them make their own decisions whether to continue. It is also advisable to inform users about problems in the documentation or installation process that prevent running your program on the target system;

2) The application should display an informative message asking end-users to contact developers or distributors if they want this feature implemented (this strategy ensures that nobody will be able to hijack your code by adding new features, while the users remain responsible for their choice).

3) When compiling your program, you may specify the OS version with compiler switches (e.g. GCC -D__GNUC__=4). It is also advisable to compile separate executables per OS version if they do not depend on any global data structures or singletons;

5) If the application implements a GUI, it must correctly handle all state transitions and always present an informative error message when end-users try to perform forbidden actions (such as shutting down the computer while the application has unsaved data). Also, react native dialogues where possible (they are easier to use than custom-made ones);

6) On Windows XP/Vista/7 and Mac OS X 10.7 (Lion) and later, the platform will not allow writing to read-only files located in system folders unless the application is correctly digitally signed with a trusted certificate or has administrative privileges. Therefore, if your program must modify these files, it should either be installed as an administrator, which grants you the right to write to these locations, or it should use some cross-platform API for creating/changing ACLs on files that are supported by all modern android platforms.

7) Do not assume that CRT functions are thread-safe - always protect their usage with critical sections if used from more than one thread simultaneously. Also, do not assume that std::string is thread-safe, i.e. do lock access to string internals if your code is used from more than one thread simultaneously.

8) Don't implement custom memory allocators without an excellent reason. If you decide to do this anyway, it is essential that at least the following features are implemented: all allocations must be aligned to their natural boundaries (e.g. 16-byte alignment for SSE data on x86); all free() calls must return memory to the system rather than just resetting some internal pointer; if your allocator uses unique pools for thread-local objects, don't forget to use TLSF or lock-free algorithms for cache locality and maximum CPU utilization; the user should be able to specify minimum and maximum amount of memory reserved for each pool - alternatively, there should be a static global object that stores this data. The list above includes many other miscellaneous tips; please check them out!

9) If you need to do I/O in a separate thread, remember that the operating system might take this thread away (e.g. when memory pressure or any other task with high priority becomes present). You should always protect critical sections of your code which are executed in the worker threads using mutexes - do not take risks and implement alternatives such as posting tasks to message queues. This is part of the "Data Execution Prevention" security feature introduced in Windows XP SP2/Server 2003 SP1;

10) When opening files through relative or full paths containing spaces, use standard CRT functions rather than custom wrappers. The latter will not allow the user to use paths with spaces in them - while this may appear as a reasonable limit, it can be circumvented by inserting particular paths into the binary resource section of the executable (i.e. "RiskyPath" = "%temp%\myfile").

11) If your program requires end-users to provide additional files along with its installer, you should always include checksums inside these files' headers and validate them upon loading - unauthorized modifications are widespread when distributing software for end-users. When choosing what algorithm to use to calculate checksums, remember that a simple algorithm like CRC32 is fast enough for typical applications. Still, cryptographically secure ones are necessary if your data is valuable.

12) Do not implement "quick fix" code without considering the long-term implications. For example, do not replace all uses of malloc() with HeapAlloc() if you plan to switch to a different memory allocator in the future - this will prevent you from changing your mind, increasing compatibility problems and may result in portability issues.

13) When using file copying functions that accept wildcards (e.g. FileCopy() or _tcscpy_s), always use the whole path along with the filename when matching paths because users expect this behaviour, and any deviations may surprise them. Furthermore, make sure that these functions are thread-safe if they are used concurrently from more than one thread - while it is possible to do this, it requires much more work than it seems.

14) Do not use too-short or too-long passwords - the quality of all randomly generated passwords is determined by their entropy, i.e. how many bits are needed to encode them reliably. While 12-character long passwords composed of extended ASCII characters may seem secure enough, they have less entropy than 15-character passwords derived from a 7-bit ASCII set (which you should generally aim for). If your password hashing algorithm allows specifying the length of the input text, always prefer longer rather than shorter inputs because otherwise, you risk introducing vulnerabilities due to accidental truncations, e.g. in case someone modifies an input parameter before submitting your form containing this field without your knowledge.

15) If you are developing code that needs to run in multiple threads concurrently, always use thread-safe versions of I/O routines (e.g. open() or _wfopen()) because otherwise, the entire application is likely to fail when executed with insufficient privileges (i.e. not elevated via UAC). This includes handling temporary files and directories if your program creates them - while it may be possible to leave this task for other components, do not take the risk by relying on potentially buggy implementations inside third-party libraries, including system calls such as tmpfile().

16) When performing certain memory operations, never assume anything about the heap's state unless you have complete control over its initialization (such as in a particular initialization function). Furthermore, handle heap "corruption" by restarting the program or showing a message box to tell users about problematic memory contents. Finally, in case your program must run in multiple threads without having complete control over their initialization (and especially when using C-based APIs such as CreateThread()), always create and destroy heap objects within the same thread because otherwise, you may end up with inconsistent pointers that do not point to valid objects anymore.

17) When passing user-defined data types back and forth between various subsystems of your program (e.g. Win32 API/COM interface, scripting library, etc.), use explicit conversion routines for type safety instead of implicit ones like wsprintf(). An example would be attempting to copy the contents of a pointer into an integer, i.e. *data = 10; where you are trying to assign 10 to *data, but this does not work because the second argument is passed as a 64-bit value which cannot be stored in 32-bits.

18) When converting floating-point numbers between different formats, never use functions such as _ttoi(), _ttod(), etc., because they produce incorrect results when encountering invalid input. Instead, always use functions like strtod() because their inputs are validated internally so that unexpected errors can be reported reliably by returning NULL or throwing exceptions.

19) When developing code that involves SSL/TLS connections (especially HTTPS), ensure that your implementation is thread-safe by either using a single global object inside your program or implementing your thread-safe equivalents for certain low-level callback functions (such as SSL_read(), etc.). This is important because otherwise, you risk introducing vulnerabilities into your application that can be exploited to cause it to crash or even execute arbitrary code (e.g. in case of buffer overflow bugs).

20) Be careful when parsing dates and times - you should not assume anything about the accuracy of the system clock beyond 20ms (or even 1s if you are executing on Windows 95/98 where the millisecond resolution may vary depending on hardware), which means that you must always account for leap seconds, e.g. in case, your program needs to schedule an event to take place at a specific time.

21) When using secure password hashing algorithms such as bcrypt, script, etc., always check the output of their computations for compatibility with non-interactive software implementations (e.g. because you want to convert passwords from plaintext to binary hashes and vice versa). Furthermore, ensure that your algorithms are one-way, e.g., mixing in additional secret data when computing the hash. Hence, precomputation attacks fail if attackers do not know this extra data.

22) If you need to extract user names and hostnames from arbitrary text without introducing any external dependencies into your program beyond standard libraries, e.g. for the sake of cross-platform support or portability, you can use the following regex: /(?<domain>[-_.\w]+)\@([-_.\w.]+)/ which matches a valid email address and allows users to enter their login name via backreferences.

23) If you need to validate an IPv4 address in javascript, simply do "s = '.".join(['0', '1', '2', '3', '4'].map((c)=>parseInt(c,16))) + ".".join(['5', '6', '7', '8'].map((c){return c-'0';}));" where each hexadecimal digit is mapped into its numerical counterpart and then joined together into a string that is then parsed back into an integer.

24) If you need to extract substrings based on regular expressions, be sure to match the Beginning and end of subject strings separately so that overlapping matches are not included twice, e.g. ".*a.*" would be matched against "abcdefcdef" as ["ab," "def"], not ["ABC," "defended"] because the .* anchor applies only at the start of the whole pattern. In contrast, it is applied separately for every capturing group inside the pattern, resulting in wrong behaviour if anchored at both ends instead of just one.

25) When using hash tables with open addressing (especially linked lists), pay attention to how many hash collisions you expect. How they behave, e.g. if your hash table is intended to store integer ids, bear in mind that you might experience a linear number of collisions as the table fills up. Insertions start failing unpredictably due to race conditions - linked lists tend to perform better for this use case because their amortized O(1) insertion complexity still applies even with many collisions. In contrast, people often forget that linear probing incurs quadratic complexity when the table is nearly complete (e.g. lots of collisions).

26) When programming in languages that lack unsigned integers, make sure all your loops terminate because relying on signed arithmetic can cause infinite loops, which are very hard to debug due to undefined behaviour at runtime (e.g. int x = -1; while (x < 0) { do_something(); }" would loop forever on a 32-bit system).

27) When using binary search with open addressing, be aware that you will perform cache misses every time a probe fails. Another one needs to be performed, e.g. in a hash table with 100 buckets, if every bucket contains precisely one element, then the number of probes will double per insertion, which can cause quadratic probe cost for large tables as well as high memory usage due to lots of caches misses - try to split your data set into several smaller hash tables with fewer elements per bucket instead.

28) If you need to use efficient substring matching or longest common subsequences algorithms in a single-pass streaming manner, you can use the following simple implementations:

substring_match("a","ab",1) = 1 + match("ab") => 3

LCS("hello","hello world",LCSlength) = LCSlength

29) If you need to generate random numbers that are safe for cryptographic purposes (i.e., state should not leak out), steer away from using Java/C/C++'s PRNGs because they do not meet these requirements e.g. java.util.Random rng = new java.util.Random(); byte[] b; while(true){ b=rng.nextBytes(); ... }" might very well produce numbers that are correlated, making the resulting state "leak" over many invocations.

30) If you need to present an interface for users that other developers can quickly implement, consider using existing open source components via plugins instead of coding everything from scratch because this will save you a lot of time and effort, e.g. if your app is essentially just drag-and-drop functionality, consider using a standard widget toolkit with a drag-and-drop package as a library instead of reinventing the wheel - otherwise others might not want to use it due to unnecessary complexity or lack of features that come out-of-the-box (e.g. input validation).

31) When writing low-level multithreaded code, make sure you never use race conditions, e.g. whenever you need an atomic operation, make sure that it is adequately synchronized using some mutual exclusion lock - this could be a simple mutex or a more complex transactional construct which allows for atomic commit if everything works as intended or rollback in case of failure.

32) When designing a multithreaded application, always focus on linear scalability instead of parallelism because the former can often achieve much better throughput without draining too many resources from the machine. In contrast, the latter pretty much always results in deadlocks and stalls after reaching a certain saturation point depending on how well the work divides among available CPUs/Cores (e.g. dividing work evenly among three cores might be 30% faster than optimizing for four cores which in turn would be 50% faster than five cores - keep it linear or your system will start having serious performance problems).

33) When designing a concurrent algorithm, try not to communicate through shared memory if message passing can do the job without complicating things, e.g. when using multi-threading, make sure you don't use atomics unless necessary (e.g. atomic operations are usually much more expensive than their non-atomic counterparts and should only be used if they greatly simplify your implementation) because lock contention between different threads is often inevitable due to resource limitations (e.g. each CPU core has its cache) and can adversely affect the scalability of concurrent applications (i.e., two threads will often occupy a core for a longer time if they are contending for a lock instead of one thread taking the entire core, which is what would happen if each was running on its own).

34) When using message passing for interactions among different threads/processes, make sure you don't communicate through shared memory unless necessary because this can lead to race conditions and deadlocks - instead, use message brokers (e.g. RabbitMQ) or broadcast channels that allow cluster-wide broadcasts to avoid complex synchronization mechanisms that might become a performance bottleneck, e.g. when multiple processes need access to an event log to do some aggregation:

35) When designing concurrent applications, always use asynchronous messaging as opposed to synchronous invocations because the latter is often more expensive due to thread context switches and locks that might form a bottleneck - when you have many threads invoking a function, it's usually better to run them all in parallel and aggregate the result from an event queue instead of serializing invocations (e.g. calling "Flickr.photos.NetInfo" synchronously will likely block other requests while holding an exclusive lock around the entire method invocation, whereas queuing these requests up into a messaging system like RabbitMQ would allow other Flickr API calls to be serviced concurrently).

36) When designing concurrent applications, always try to minimize interference by avoiding shared state whenever possible, e.g., keeping data in memory unless necessary. This can easily lead to race conditions that are hard to debug or recognize within a test matrix.

37) When writing concurrent applications, always use local storage instead of shared state whenever possible, e.g. don't rely on external factors outside your process when building your application - this will significantly simplify the architecture by making it much harder for developers to introduce race conditions into processes/threads which could lead to bugs that are often difficult to reproduce and resolve (e.g. data races).

38) When using transactional memory, make sure you take special precautions in case an exception happens during transaction commit because this can leave the system in an invalid state - one way to do it is by surrounding critical sections with try-catch statements or even better syntactically surrounding each method invocation within the transaction with its try-catch statement (something like "try { delete t; } catch (Exception e) {}").

39) When using pessimistic locking, make sure you always lock the minimum number of records required because this will give other processes/threads more chances to use different data - e.g. don't acquire locks on all records within a database table until these are used (e.g. acquire locks around groups of 10 records at once instead of acquiring them for each record at the Beginning).

40) When writing concurrent software, if process A has to wait on an external resource held by process B, which in turn is waiting for another external resource held by process A, deadlock is probably going to happen unless you design your application carefully with some master-slave relationship between these two processes - e.g. if process A has to wait for another resource held by process B, it has to yield control back to the system (release the lock) so that it might become available for other processes/threads which are not blocked on it; waiting indefinitely on a specific resource is generally OK unless you have multiple threads accessing the same resources within a loop and none is willing to give up its claim on them (in which case deadlock becomes possible).

41) When designing concurrent software, always use asynchronous messaging as opposed to synchronous invocations whenever this makes sense because the latter often causes thread context switches which are expensive due to thread scheduling - if you have many threads invoking a function, it's usually better to run them all in parallel and aggregate the result from an event queue instead of serializing invocations (e.g. calling "Flickr.photos.NetInfo" synchronously will likely block other requests for this duration while holding an exclusive lock around the entire method invocation, whereas queuing these requests up into a messaging system like RabbitMQ would allow other Flickr API calls to be serviced concurrently).

42) When designing concurrent software, always try to minimize interference by avoiding shared state whenever possible. This can easily lead to race conditions that are hard to debug or recognize within a test matrix - e.g. don't rely on external factors outside your process when building your application.

43) When designing concurrent applications, never rely on the order of method invocations. This can lead to bugs that are often difficult to reproduce and resolve (e.g. missed transactions in a transactional memory system).

44) When writing concurrent software, always try to minimize interference by avoiding shared state whenever possible because this can easily lead to race conditions that are hard to debug or recognize within a test matrix - e.g. don't rely on external factors outside your process when building your application - this will significantly simplify the architecture by making it much harder for developers to introduce race conditions into processes/threads which could lead to bugs that are often difficult to reproduce and resolve (e.g. missed transactions in a transactional memory system).

45) When writing concurrent software, always use asynchronous messaging as opposed to synchronous invocations whenever this makes sense because the latter often causes thread context switches which are expensive due to thread scheduling - if you have many threads invoking a function, it's usually better to run them all in parallel and aggregate the result from an event queue instead of serializing invocations (e.g. calling "Flickr.photos.get info" synchronously will likely block other requests for this duration while holding an exclusive lock around the entire method invocation, whereas queuing these requests up into a messaging system like RabbitMQ would allow other Flickr API calls to be serviced concurrently).

46) When writing concurrent software, always try to minimize interference by avoiding shared state whenever possible. This can easily lead to race conditions that are hard to debug or recognize within a test matrix - e.g. don't rely on external factors outside your process when building your application.

47) You should avoid using blocking synchronization primitives in mostly CPU-bound applications unless you have extremely stringent requirements - they will slow down the entire application significantly due to context switches between threads (e.g. when using MySQL with thread pooling, async queries may scale much better).

48) Thread pools allow you to take advantage of multiple CPUs/cores by splitting long-running tasks into smaller work units and executing them concurrently on a set of pre-configured threads. This is more efficient than launching a new thread for each task because it saves on the overhead of thread creation/destruction and context switching between threads, which are relatively expensive operations compared to running many tasks on a small number of threads (CPUs often support hardware-based threading, so if you have N cores available, it's more efficient to run N-1 tasks per core rather than creating N+1 lightweight threads).

49) You should avoid using blocking synchronization primitives in mostly CPU-bound applications unless you have extremely stringent requirements - they will slow down the entire application significantly due to context switches between threads (e.g. when using MySQL with thread pooling, async queries may scale much better).

50) When writing concurrent software, always ensure that your mutexes are properly bounded because using an unbounded lock is equivalent to choosing the system scheduling priority of your process(es) - this can allow even shallow priority processes to run amok and starve out all other requests for CPU time after they are done holding a lock. When creating threads, it's usually best to use a tiny priority starting point to avoid this problem (e.g. FIFO or round robin with thread priorities below normal rather than raising them above average).

51) When designing concurrent applications, never rely on the order of method invocations. This can lead to bugs that are often difficult to reproduce and resolve (e.g. missed transactions in a transactional memory system).

52) When writing concurrent software, always avoid blocking operations whenever possible because they often lead to deadlocking - this is not a problem with async systems, which can use I/O for communication between processes.

53) You should never rely on the order of field accesses when building concurrent systems involving multiple threads sharing mutable fields without using synchronization primitives such as locks or atomic types - this will almost certainly lead to race conditions that are extremely difficult to debug within a test matrix.

54) You should avoid using blocking synchronization primitives in mostly CPU-bound applications unless you have stringent requirements - they will slow down the entire application significantly due to context switches between threads (e.g. when using MySQL with thread pooling, async queries may scale much better).

55) Asynchronous systems provide an abstraction for services that can be controlled via long-running callback functions - this provides a programming model which is often more efficient than traditional threaded concurrency for I/O bound tasks such as HTTP servers and interactive CLI interfaces (they also allow the programmer to tune the granularity of request processing), while still allowing them to use threaded concurrency where it's needed (such as in event dispatchers).

56) Prefer driving operations within your program from a single thread and offloading blocking operations onto other threads only when they are essential - this is more efficient than spawning one thread per operation due to the overhead of subsequent threads.

What is cross-programming?

Cross-programming is the process of creating programs on different platforms.

In cross-platform native app development, all the code is written so that it can be executed on any platform without modifying anything.

Cross-platform applications are programs that have been developed to work on more than one operating system or hardware platform. In other words, they are applications that run consistently well on various computing devices and computer operating systems. One of the goals of writing a program in this way is to make duplication of the source code unnecessary for each platform where it will be executed, thus reducing development time and cost.

Few examples: 1) Many web browsers available in the market 2) All android cross platform apps 3) All iPhone native apps 4) All windows web apps 5) Many other native programs.

Creating this web page is to provide the guidelines for writing cross-platform applications. I have collected these guidelines from different resources available on the internet. These are common mistakes made by developers while writing code. For example, many existing codes work well on Windows OS but do not work well on Linux or macOS. As a Software Developer, you need to understand why it's happening and how you can solve them. 1st article will deal with threads and concurrency, 2nd article will deal with GUI programming using Qt5(QML), 3rd part will talk about database persisting using QSQLITE3 open source SQL Database Engine. If you are interested, then please subscribe to this blog.

Let's first understand why these problems are happening? It is widespread that Windows programs work on Windows and Linux programs work well for Linux users when you think about it. The reason behind this phenomenon can be explained by any one of the following reasons: 1) Developers who write code for windows do not know how to handle threads, or they don't bother to implement proper threading concept because it will slow down their application which may result in bad user experience (UI responsiveness). 2) They think writing code for two platforms is enough; if someone has expertise in C++, then he should be able to develop an application that works fine across all platforms, including Windows, Linux, macOS, etc. 3) Many developers think a large number of windows users will help them generate a higher return on their investment (ROI).

To fix the above problems, you need to learn the following things: 1) What threads are 2) How to write threaded code 3) How to handle concurrency 4) Synchronization between threads 5) Threading models 6) Differences between Win32 and POSIX APIs 7) Advantages and Disadvantages of different threading models 8 ) When should be I/O bound tasks placed in a separate thread, etc.

The reason why cross-platform mobile app development process is so tricky is that writing good multithreaded or multi-processing programs requires programmers to have skills that must also be used for non-multi-threaded applications. Thus the same developer writing an application for Windows cannot necessarily write a multithreaded or multi-processing program that will run on Linux, at least not without substantial effort.

In general, one can say that programs that are CPU bound should be written as single-threaded applications because there is no benefit from using multiple threads, and it would make them slower. On the other hand, if your program is disk-bound, you should either use asynchronous I/O (non-blocking I/O) to improve the responsiveness of the user interface or first write code using synchronous I/O to gain a better understanding of the performance bottlenecks before trying out asynchronous I/O solutions.

Synchronous programming directly interacts with hardware, making it more efficient than asynchronous programming. Further, there are different types of Synchronous programming - OOPs(Object Oriented Programming) based on sync class C++11, etc.

Asynchronous programming - Asynchronous I/O operations are independent of each other, so they can happen concurrently without interfering with one another. They will not block the main program flow, thus freeing the CPU to other processes. So it is more efficient but less secure than synchronous programming because it is often difficult for a programmer to code in a concurrent environment where he has to deal with asynchronous objects that call him back at arbitrary times. It requires higher coding skills, resulting in Null Pointer exceptions or memory leaks due to improper object clean-up after use.

Concurrency is the composition of independently executing processes. The processes may be executing on separate CPUs, on separate cores on the same CPU, or even overlapping on the same core as long as they do not interfere and can always resume execution immediately after finishing their task.

Concurrency has become a critical topic in programming, especially for those who program for multi-core processors and multi-processor systems. But why? Here are some reasons: 1) CPU manufacturers have increased the number of cores/processors to improve performance. Still, programmers must rethink their entire algorithm to maximize all the available resources rather than waste them improperly synchronizing between threads. 2) Nowadays, almost all devices require real-time multithreaded applications due to the increasing need for quick user responsiveness. For example, you click on a button that shows some time-consuming animation, but the application must not become unresponsive. Otherwise, users will get frustrated by seeing no response from your application. 3) A new type of programming called Reactive Programming has come up where concurrency plays an important role. It works using asynchronous callbacks based on concurrent tasks (not necessarily multiple threading).

How does one make sure their program is implemented correctly for multi-processor architectures? First of all, don't try it unless you have at least two CPUs or multi-core processors because there's no point in doing so if you only have one or a single-core processor.

Also, it is highly recommended that you use a monitoring utility because once you have decided your program is correctly threaded for multi-processor architectures, there's no going back. The application will run as a multithreaded application because even if you try to change it to a single thread-based process, there will be synchronization issues that will result in deadlocks or race conditions.

So if you want to keep your code cleaner and make sure there are no bugs due to missed up synchronization between threads, don't even think of writing a multithreaded program unless it is necessary because most programmers who begin their programming career following sequential design methodology may find difficulty in understanding how multiple tasks can be handled concurrently by the Operating System.

Here are the main steps you should take when writing a multithreaded application:

1) Create a separate Thread for every concurrent task and ensure they don't share any data (exceptions can be made if necessary, such as using queues).

2) All processes/tasks should be initiated in the primary () method, and wait(timeout=0) should be used instead of getting () to allow the thread to run until it has nothing else left to do.

3) Make sure that all your objects, functions, methods, and constructors are correctly synchronized. This is where most programmers get stuck without fully understanding what's going on behind the scenes while their program runs parallel.

4) The most crucial step is to ensure that your tasks do not lag behind the main() thread, and every task must be completed before Main Thread terminates. If any of these tasks take time, consider using a timer.

Code Reusability

When writing multithreaded code, the chance of reusability of code is very high because each task usually has its functions and methods for performing specific tasks such as iterating through a list, creating a GUI thread, etc.

So you can easily create different threads from your primary () method, which will run without interfering with each other, and can always resume execution immediately after finishing their task.

For example:

class api { public: void add(item*); // this function will be executed in separate thread since it returns nothing }; void main (){ api *apiObj = new api(); // here we have created separate thread for 'add' function to be execute asynchronously item *myitems[] = { new item(5), new item(10), new item(15) }; apiObj->add(&myitems); // this will call the 'add()' function in separate thread }

Not only does multi-threading have many advantages, but it also has disadvantages too. The major disadvantage is that writing a concurrent program is very difficult. In addition, the chance of an error increases when you create threads if your program is not designed correctly.

Another disadvantage is the overhead created when running multiple threads because, at any given point in time, most systems have to switch between threads, which means data would have to be moved from one memory location to another, which results in slower execution of code and use of more CPU cycles which can reduce overall system performance.

So now you know how to write multithreaded applications, which is good, but if you want to create a high-performance application, it is not enough that your application runs on multi-processor architectures; you have to optimize your code for better system performance.

This can be achieved by using various tricks and techniques suggested in this tutorial.

The first thing I would recommend is the use of C++ Templates because they generate fast code for any type or class function or method parameter without having to specify anything else except compile-time arguments, so calling a template instance will result in generating an optimized version of the user-defined code according to their specifications (or for example for several threads). Here are some tips on what kind of code can be optimized with templates:

1) Templates can optimize the code by performing inlining function calls (and not only functions but also methods and constructors etc.). This means that a function call will be directly integrated into the calling code instead of a separate procedure call resulting in a faster speed.

2) The template class/function should have an access specifier to be used as a private type inside other classes. This allows you to implement support for multi-threading and use other optimization techniques such as delayed instantiation, which creates the type only when needed. The constructor would also reduce overhead because creating an object during runtime takes longer than creating one at compile time.

Templates can also implement pre-computed values by specifying a range of inputs. Then, the compiler would only have to calculate those specific input values instead of generating different outputs for a different type of input which is very useful in creating hash tables and lookup tables etc. This technique is primarily used in cryptography, but you can use it to lookup table operations.

Easy deployment & maintenance

Cross-platform applications can be compiled on different operating systems and run without problem.

Managed C++ is a language that makes coding easier for developers because they do not need to worry about memory management; the GC (Garbage Collector) will take care of that, and it also helps in speeding up execution time since you don't have to wait for your data to get deleted. Resources get freed and all that stuff. It also allows you to quickly identify memory leaks because the GC will automatically detect them. The only disadvantage of using Managed C++ is that it requires an extra layer between your code and OS, resulting in performance loss. Still, it's worth using if you get benefits such as easy memory management etc.

Reduced costs & resources

With Cross-platform development, you do not require multiple programming languages to develop mobile applications for different operating systems.

Cross-Platform Open Source Development has initially published on Improve Your Coding in the Beginning and was syndicated with the author's permission.

Uniform design

All programs in multi-platform development are designed with uniformity in mind. This means that the program's interface remains the same in all operating systems.

For any object oriented programming language, to achieve cross-platform compatibility, you have to do some extra coding which can reduce execution time or use platform-specific functions which are available only for specific operating systems, but this should not be a problem if you stick to C++ because it helps to implement abstractions and gives you more control over what gets executed at runtime so this way you can quickly write code that works on multiple platforms without worrying about any performance penalty or other drawback.

The main advantage here is having multiple APIs/APTs available for your app to run on different devices, so developers will have to spend less time researching what APIs can be used for which platform and instead focus on writing code since the interface remains the same.

For example, suppose you are using Managed C++. In that case, you do not need to worry about memory management because the GC takes care of allocating memory dynamically at runtime, so you won't have to worry about any memory leaks or performance penalties.

Wider market reach

Writing a program once and then getting it ported to other operating systems to run your code is way more convenient than writing separate codes for different platforms. Usually, the cost of developing an application increases with time due to increased workforce/hours spent on coding, so this way, you would have saved money if you had gone for cross-platform development from the start instead of going with platform-specific development because now you will have to develop a new version just for a single platform which means more cost and fewer earnings.

Abstractions implemented by C++ help decrease design costs and implementation costs because they provide the cross platform app framework required by any multi-platform app. They include data abstraction, object orientation, etc. The language also gives developers complete control over what gets executed at runtime. Hence, it allows the developer to change any part of the code without breaking anything.

1. Code written for one platform will not work because different APIs/APTs available for different operating systems.

2. Cross-platform apps don't provide as high performance as native apps but can be used if you need portability over other platforms and are willing to compromise on performance a bit (as mentioned, this can quickly be done using C++).

3. If your app requires advanced features like access to low-level memory management, then there is no other alternative except writing separate codes for each platform which means higher development cost etc... This problem can be solved by using Managed C++, so you can write low-level code using C++ and then let the GC take care of memory management at runtime to give portability over multiple platforms.

4. As mentioned earlier, for Cross-platform development, multi-platform APIs/APTs should be used. Unfortunately, all these API's/APT's come with a price, that is, a reduced performance which means your app will not perform as well as it would have had you gone with platform-specific development.

5. certain technologies can help improve cross-platform development like Microsoft Portable Library (PBL), Metrowerks Codewarrior (MWC), Visual C++ Compiler for Java (VCCJ). But again, there is a catch here; these technologies are pretty expensive.

Cross-platform frameworks based on programming languages like Mono, .NET, etc...

Also, provide a solution to this problem because they work differently than the other frameworks we mentioned earlier. You can write code using any programming language and then compile it into binary files that run on multiple platforms.

However, go with these solutions. Your app will still perform at a much lesser speed than what you would have gotten had you chosen platform-specific development, so choosing the right technology is essential.

What is the best cross-platform mobile development?

Since choosing the proper cross-platform framework is essential, I recommend you look at all the available options and choose the one that suits your needs best.

After going through this article, if you think we missed out on anything or have any questions, please don't forget to comment below, and we will get back to you as soon as possible...


Geolance is an on-demand staffing platform

We're a new kind of staffing platform that simplifies the process for professionals to find work. No more tedious job boards, we've done all the hard work for you.


Geolance is a search engine that combines the power of machine learning with human input to make finding information easier.

© Copyright 2022 Geolance. All rights reserved.