PureBasic
Last update: Sep 2023, PB 6.02
This page has been visited 55772 times.
Did you ever hear of Turbo Pascal 3.0 ?
While Microsoft was producing its sophisticated but bulky compilers, in 1986 Borland introduced Turbo Pascal 3.0.
It was written in assembly, it was blazing fast and in less than 40 KB you had a compiler, an editor, a linker and a debugger all loaded in RAM at the same time.
It was something that really impressed me, computers were not as powerful as today and TP3.0 was a joy to use: everything was immediate and the programming cycle [write code -> compile -> test -> repeat] was so effortless it was like using an interpreter magically running at the speed of compiled code.
In 2004 I stumbled on PureBasic after using many generations of Visual Studio (Visual Basic and Visual C/C++) and I was hooked by experiencing again, after all those years, a sensation similar to the one TP3.0 gave me in 1986.
Small, quick, simple.
I was happy !
PureBasic is a language, you guessed it, sharing some common ground with a very large family of BASIC dialects.
It's available for Windows, Linux, macOS and Raspberry.
It's possible to write cross platform programs by just using PB statements and libraries, if you limit yourself to only the common set of commands and libraries available for all the OSes.
But realistically you often need to write at least some platform specific code here and there, using the target OS APIs.
On macOS this requires some acrobatics since Cocoa it's built with Objective C in mind.
The PureBasic's IDE is one good example of a fairly complex cross platform application and it's written in PureBasic. It's not really identical on all the platforms but it's reasonably close, so you get an idea of what can be done.
Also recently the IDE has been made open source! Now you can contribute to the bug fixing or add new features, or simply look the source to learn something from it.
It's available here: PureBasic on GitHub.
As any language has its strong and weak points, and most of them are subjective since not everyone need (or like) the same level of power or complexity so what I find important or irrelevant can be very different from what you think.
But beyond my opinions, there is a lot of good information in here, and if you just discovered PureBasic this page can point you in various directions you can explore further and most importantly a lot sooner.
Unless explicitly stated otherwise these notes are relative to the Windows version, both x86 and x64, since that's the OS I use most.
Let's start.
PureBasic's best features:
- Executables are very small for today's standards and you don't need to ship a runtime library with your program.
- Writing a simple GUI with its gadgets is really easy, and all the gadgets maps to native controls on the various OSes, so they look right.
- It can create a single EXE, with extremely reduced dependencies (usually only the DLL shipped with the OS unless you use the 3D commands requiring the supplied OGRE DLL).
- It can be easily used to create portable programs, self-contained in their home directory, not requiring an installer or tampering with the registry.
- Generated code is quite fast. It can produce ASM or C code and than compile it using FASM or GCC respectively.
The C backend is a recent addition, and thanks to the GCC compiler you can expect a material gain in execution speed in most cases, especially on 64 bits.
- It also supports inline ASM or inline C depending on the backend selected for the compilation.
- The compiler can generate code for 32 and 64 bit, and the language makes very easy to write a single source targeting both the architectures.
You need to install two instances of PureBasic though, one for 32 and one for 64 bit.
- The vast majority of Windows API functions are supported as native commands (you can always import the missing ones similarly as you do in other languages).
- Compilation time is blazing fast (it's a single pass compiler and its syntax is very simple, so this helps).
- The editor is pretty good (based on Scintilla), the debugger is very good.
- In debug mode is possible to examine the contents of memory buffers and the state of windows, files, threads, gadgets, sprites, XML data, even visualize image objects with their alpha channel if present. All this is extemely handy.
- The whole package (compilers + libs + IDE) it's compact (around 100 MB) and self contained. You install/configure it in minutes and you can go in and out from it in seconds.
- It does support projects with multiple target builds (a project is a container for the many related source files you need to manage when writing bigger programs), but their use it's entirely optional.
- It comes with a kind of memory debugger, called Purifier, extremely useful to spot problems with pointers and buffer overruns.
Currently it fully works only with the ASM backend, but hopefully this will change in the future.
- Another very useful tool included in the IDE is the session history. It saves your files regularly in a database and it can restore a previous version of them if required.
- There is a very interesting GUI object called CanvasGadget useful to build custom graphical controls on it.
This is especially nice to build cross-platform gadgets looking the same on different OSes and works well with the 2D drawing library, which also supports useful alpha channel operations.
- It supports ASCII, Unicode/UTF8, multithreading all natively with specific keywords/libraries.
- You can utilize different versions of the compiler, with different backends (ASM/C), and targeting different architectures (x86/x64) from a single instance of the IDE.
You just add the desired compilers to the list of the active ones.
But keep in mind PureBasic it's not a cross compiler, you need different instances of the IDE and compilers for each platform (Windows, Linux, etc.)
- The language is somewhat an hybrid of BASIC and C, and it's easy for a beginner but not limited to the very old "BASIC" stuff.
- You can use macros to expand code at compilation time (even if PB macros can become pretty ugly fast for not trivial tasks).
- Documentation is reasonably good even if not very strict and if you have a question about the language not covered in the manual you can probably find an answer in the very active forum.
- It does natively support the usual data types supported by BASIC, plus some extra data structures like linked list and maps.
- The version 5.20 brought the implementation of modules.
The actual implementation has severe limitations but anyway they may give you the ability to isolate code in a sort of black box, to reduce name clashing at least until your luck runs out, and hide private code.
More on this later.
- It comes with a large and growing set of libraries.
Some of them are too simplistic but most are very useful and they may speed up developing time considerably.
Weaker points:
-
Until version 6.0 in 2022, slow and spotty bug fixing has been the biggest let-down for me.
For years we had long standing bugs in the compiler (sometimes 5-10 years old), but recently Fred (the PureBasic creator by the way) has started to fix them.
I welcome this change (thank you Fred) and I can only hope this is how bugs will be dealt with in the future.
-
You should consider what are the future prospects for the code you write in PureBasic today.
PureBasic until now has a proven track record of being maintained in the years and it has never been abandoned by its author and very few indie languages can say the same.
But PureBasic it's still not something near immortal like C or C++ and could stop being maintained tomorrow for many reasons.
Also it's the only compiler available for the language, you'll never be able to switch to another compiler bringing your code with you.
Furthermore the small user base and the small code repository means you have to write a lot by yourself, or translate code from other languages, or at least write the occasional wrapper instead of simply plug it in.
This may be not trivial work and can be considered instructive or wasteful, depending on your point of view.
-
You can't create static libraries from your PureBasic code. You can create a dynamic library (.DLL), but not a static library (.LIB).
You can create a static library only using C or ASM. This is officially supported and there is some terse info about it in the SDK coming with the compiler (not always kept updated in my experience).
Personally I prefer to stick to the inclusion of the full source code when needed. Compilation it's so fast it's not a problem, and I can navigate the "library" code with the debugger.
If I'll ever have the need to distribute a PB library not in source form I would certainly go with the DLL route.
-
Pointers are a little rigid compared to C ones. You can achieve roughly the same results but pointers' arithmetic it's more primitive.
For example you can't add an offset to a pointer inside an expression to access a memory location. You have to add the offset to the pointer variable and then use the altered pointer variable.
So you need make a copy of it in advance if this not what you wanted and you need to access the original value again.
For a discussion about this and other limitations see this thread in the forum.
Coming from another language all this requires some time before you will be comfortable with it.
-
Unused procedures are sometimes included in the final EXE.
The same can be said for library functions referenced inside uncalled procedures.
This is something which undermines one of the positive aspects of PureBasic, compactness.
-
Defined but unused variables are not reported at the end of the compilation.
This poses the risk of leaving a variable or maybe even an array or something bigger inside a procedure because you modified it and forgot to remove a data structure no longer used.
-
The number of events associated to the GUI gadgets is extremely limited, just compare it to what you have in other GUI toolkits.
This makes almost inevitable to use OS API calls to fill the gap. In short: do not expect to be able to write a particularly feature-rich GUI using only PB commands and the built-in supported events.
-
PureBasic does not support unsigned numbers. This is a nuisance when you have to link a C library or just want to use APIs created with the C language in mind.
You can obviously store a C unsigned int (32 bit) inside a PureBasic long (32 bits) since the binary value is correctly stored and it's just a matter of interpretation if it's signed or not, but PureBasic will still consider it a signed number for example in comparisons, leading to problems.
-
It's a procedural language, not an object oriented language. No OOP programming. Run now if you find this crazy and never look back.
According to Fred it will never change.
Anyway PureBasic supports Interfaces. With those you can call COM objects, interface to DirectX or even build your own classes from scratch, if you really want and you constantly pay a lot of attention to what you are doing.
-
While it's a lot better than old style BASICs, it's not the most modern BASIC-like language.
You don't have functions overloading, so you need to call basically identical functions with different names just because their params are of different types.
This for some kind of software (math stuff for example) can became tedious very fast.
You don't have any form of generics or templates, so general algorithms working on generic data types are mostly impossible to write.
You can't initialize an array the moment you are defining it.
This could be useful for example when you have a function where you need a local lookup table, something you could build with a static initialized array.
There are workarounds (datasections with pointers pointing there and similar stuff), but they are just that.
Sometimes, to me at leasy, it feels like being trapped in time in the late '70s on a island with no contact with the contemporary world, this if you know other languages where this stuff is commonly available.
Personally I believe language enhancements of some importance are very unlikely to happen.
-
The ability to build an ASCII executable has been dropped in PB 5.50.
You can still process ASCII data, read ASCII data converted on the fly to Unicode and create ASCII and UTF8 buffers from Unicode strings, but the string library used by the compiler will be Unicode only.
This means fatter strings and slower strings processing compared to the ASCII only build we had available until now, and a larger exe even when you do not require Unicode at all.
In some cases ASCII is still all you need, and I would have appreciate to retain the option.
You can read the opinions in favor and against this (and even some inanities) in this thread.
You can read my rants about this here, here, here and here.
You get the idea.
-
Modules were a nice surprise or so I thought initially.
They are comfortable enough when used with small sources, but as soon the lines count goes up and you need to split a module source in multiple files, it all become very clumsy.
All the procedures must be defined inline inside a module, so it's not possible to declare them in the body of the module and define them elsewhere with a module's name qualifier as part of their name (as C++ does with classes for example).
You can't even repeat the Module / EndModule pair multiple times and have the compiler merge the multiple instances into one.
What you can do is just use include files inside Module / EndModule to split the code in different files.
Of course when you look at any of those files, you don't have any idea if that is normal 'global' code or code part of a module, since as we said above they are inline and there is no name qualifier.
But these are just small complaints and I can easily put them aside, there are bigger problems.
The idea behind PB modules is inspired by the namespace concept used in other languages, but:
-
You can't access objects in the global namespace from a module.
This just makes your life miserable without reason when you need to access a global object from inside a module, for example a constant (maybe to configure a module), or a globally imported function from a library you want to use in the module AND in your main program too (think for example to OpenGL functions).
There are possible workarounds, like moving global objects inside a common module (the officially suggested solution), or passing a pointer to a global object through an initialization function in the module..
The same problem has been already solved in the best way many times before in other languages through a scope resolution operator, something like "::" or "global::", and they should have just copied it.
Since you can do something similar anyway by using a common module, a pointer, or even UseModule to import a specific module inside the scope of another one or inside the main scope, why don't just implement the "::" and let me do what I want easily without these unnecessary steps ?
Also note how residents are immune to this problem and they can be magically accessed from inside a module, something I find inconsistent.
Moreover the idea of the module as a black box, unless we are talking about trivial examples, it's just a myth.
Even using the officially supported common module mechanism mentioned above, the importing module would obviously depend on the common one.
Not only that, the importing module must also know the name of the common module it wants to import, whilst by using "::" it doesn't need to know anything.
It can just assume what it needs it's there in the global scope like any other PB command and use it by prepending a "::" to the symbol name.
Even when you use some OS APIs in your module you depend on them and if the OS version change or an API is not present anymore your module is broken because an external dependency is missing.
So instead of trying to enforce some philosophical and unreal view I would prefer to have something flexible to use in real life and to be able to choose what's best on a case by case basis.
This has been requested in the forum by some people (myself included) when modules were introduced but with no results.
-
It's not allowed to define a procedure inside a module with the same name of a PB command.
A better way would have been to give precedence to what declared in a module, and make still accessible the overridden PB command from the global namespace by using "::".
This is a problem if you want to call a function in your module like a PB command (for example LoadImage()) and more importantly is a problem if in a future PB version a name collision happens anyway
because a new PB command take for itself the name of a function you defined in your module.
Also, if your function is not a private but a public one, this will cause your interface to break.
If what defined in your module had priority over the new conflicting command, your public interface would continue to work exactly the same, and you could access the new PB command from inside the module by using "::" if required.
This is a problem not just for the functions but for definitions in general. For exampe if the residents contain a structure with the same name you would like to use in your module you can't.
This creeping and growing namespace pollution is always waiting to bite you, when you are forced to use some silly name because the one you wanted is already taken.
For a namespace-like feature this is really silly.
In short PB modules, while sometimes usuable to good effect, are honestly an half baked answer to a problem they don't solve properly.
... and some other points:
-
The official visual designer is a moving target. Once was a separated project, for a while was practically abandoned, and now there is a new version trying to be more tightly integrated with the IDE.
I don't have a definite opinion on that because I do all my simple GUIs by hand, but if it's important to you, you better give it a whirl and see for yourself.
-
The support for accelerated 2D graphics (DirectX 11 under Windows) it's simple but effective (sprites, 2D drawing, mouse and keyboard libraries) but I tested it superficially simply because I like OpenGL and part of the fun resides in writing my own library for that.
-
There is a support for 3D graphics through an adaptation of the OGRE engine to make it usable through a flat API. It's simplified compared to using OGRE directly but it's moving forward with frequent additions.
Using it requires the distribution of a customized OGRE DLL with your program. Again I don't really use it so you better ask about it to someone else in the forum.
-
Being a language still developed a new compiler version can break your old code.
In my experience the changes you have to make to your programs are really minor, I never spent more than a couple of hours to update a program to a newer compiler version.
Wrapping it up
I think PB can have its place, it's quite unique and I don't know of anything else directly comparable.
It can be a handy tool if you are an hobbyist or a nostalgic programmer, if you miss the nice days of Turbo Pascal 3.0 like myself, or if you simply like BASIC languages and you want a small, fast, compact environment to code in without distractions or layers of added complexity, if you just dislike OOP,
or if you normally use more complex languages and you are looking for a tool to quickly write support utilities, etc.
Some aspects of it are very nice and at the same time some of its shortcomings are annoying to live with.
Don't expect the core language to meaningfully evolve, I gave up on that and I no longer waste my time in the forum discussing what could be done and how.
If you have certain expectations which are not currently reflected in the language, you should move on to something else even at the cost of an initially steeper learning curve.
If you are happy with its current state, you can enjoy the positives I listed before and even have some genuine fun with it, I certainly do.
Now it's time for you to try it and make your mind about it.
Read the subsections of the forum dedicated to reporting bugs, look at the kind of reported bugs, how old they are, the replies or lack thereof, and read the manual once from top to bottom (very few do it).
Most of all, try the whole package for a while, create a small but real project with it and consider the pros and cons of your experience.
Myself, I have a lot of fun with it, so maybe I'll see you in the forum !
Oh, one last thing: PureBasic has a brother now! If you are interested in PureBasic but also in the Web, Android and iOS you should consider it.
Some links
PureBasic : The official home page of the language.
PureBasic forum : The official english forum.
PureBasic Team Blog : The official blog maintained by the PureBasic's developers.
Interview with Frédéric 'AlphaSND' Laboureur (fred) (2005) : The creator of PureBasic.
Interview with Timo Harter (freak) (2009) : The other PureBasic's developer.
Interview with Frédéric 'AlphaSND' Laboureur (fred) (2012) : A more recent interview with Fred.
PureArea (André Beer) : Home of CodeArchiv.
PureBasic Survival Guide (blueznl) : A great guide you can use to complement to the official PureBasic's documentation while learning the language.
A PureBasic program using OpenGL running on Windows 10 on my PC.
Once upon a time you could have sent me an e-mail by clicking somewhere around here ....