In QB64, when linking with an external static library `nm.exe` is used
to determine whether the symbol being used is either a C or C++ symbol,
which determines how the function should be declared in the C++ code.
Unfortunately on Windows the `SHELL` command for `nm.exe` is missing the
`cmd /c`, which means the redirection does not work and consequently
we're unable to find the function declaration via `nm.exe`, which causes
the compilation to fail.
In addition to fixing this, I added tests for `DECLARE STATIC LIBRARY`
for all supported platforms.
Fixes: #112
The math evaluator already contains a few checks for zero when we're
executing operations that could cause a `Divide By Zero` error, but the
actual value input is floating point and can get rounded to zero when
the operation happens. This effectively allows zero values to bypass the
checks to prevent them from being evaluated.
To avoid the error completely we need to check that the rounded value is
not zero, rather than the initial value. FIX() is used to make sure
values such a `-.1` round to zero and are avoided. I also applied FIX()
when we actually execute the operation, just to ensure the same
behavior.
We shouldn't allow mutex lock/unlock to silently do nothing if NULL is
passed, as that is very likely a bug. Beyond that the Windows version
doesn't do this, so it's inconsistent as well.
This moves a lot of the preprocessor flags for what compiler/platform
we're using into a libqb-common.h header inside the libqb/include
folder. This gets included at the top of every libqb .cpp file, and is
intended to be fairly small, providing only necessary things like
_WIN32_WINNT (which needs to be defined before including <windows.h> or
friends).
The Buffer API implements an append-only buffer, where you can write to
the end or read from the beginning. Data that is read is discarded from
the buffer, and you can query the buffer to get the current amount of
data inside.
Internally the buffer API is implemented as a chain of separate buffers.
This is done to avoid having to resize the existing buffer, which is
expensive. We keep track of where the reading currently is, and discard
the internal buffers after all the data in them is read.
Completions are basically a oneshot flag, which provide a `wait()` call
that blocks until 'finish()' has been called on the completion.
The nice aspect of completions is that because it is a oneshot the order
does not matter - if 'finish()' is called before 'wait()' then 'wait()'
returns immediately. It makes the logic for waiting until a thread is
done finishing up some work easy to implement.
This adds generic APIs to libqb for handling thread's, mutex's, and
condition variables. On Linux and OSX these are implemented via the ones
provided by pthreads. On Windows they're implemented via the ones
provided by the Win32 API.
For compiling, the code itself is not conditional, but the Makefile
includes logic to decide which implementation to pick.
Note that it would have been nice to simply use std::thread and friends
from C++11, however using them on MinGW appears to be a bit messy. Since
using the Windows ones directly isn't that hard this was an easy compromise.
This is a very simple testing framework for writing and running tests
for C++. The basic idea is that a `struct unit_test` is defined for
every test function to run, and then the test framework simply runs them
one at a time, printing various information about the tests out as ti
goes.
There are also various 'test_assert*()` macros to assert the value of
things while running the tests. They verify a value is equal to an
expected value, and if not it fails the tests. The more specific
assertions like the `ints` and `buffers` ones also print out useful
information on what the expected and actual values were, to aide in
debugging.
This is a fairly flexable build framework for building C++ test
executables. The executables are defined in a list and each can be given
its own set of source files, compiler flags, linker flags, etc. The
test source files are compiled together at the same time to allow any
necessary files to get applied at compile time.
Later a script will run through the test executables.
This sets up a few different flags we'll need for the conditional
compiling, and also sets the C++ standard to gnu++11, which effectively
just matches what we were implicitly using before.