Clientlib migration guide
How to use the updated Clientlib
Cheerp nightly only
Clientlib (clientlib.h
) has been updated with improved type information, addition of modern APIs, integration with C++ coroutines, and more.
For more information, see this blog post.
This guide shows you how to migrate to the updated Clientlib.
Common errors
This is not a complete list, but it should provide plenty of information to get you started.
Class type parameters must be pointer types
For template classes, such as client::TArray
, if the type parameter is a
class type, it must be a pointer. Primitive types must not be pointers.
Any attempt to use a non-pointer class type as a type parameter will result in an error.
Issues with static_cast
Using static_cast
to cast between client types may result in an error.
Most of these issues can be fixed by replacing static_cast<T*>(object)
with
object->cast<T*>()
. If object
has a dependent type, you may need to use
object->template cast<T*>()
instead.
Class template requires template arguments
Some types that were type erased before now require template arguments.
The best way to resolve these errors is to add the appropriate type parameter.
For example, getName
should probably return
client::Promise<client::String*>*
.
When the appropriate type is unclear, or when the template type must stay type
erased for other reasons, you may choose to use
client::Promise<client::_Any*>*
or client::Promise<client::Object*>*
.
Some of the types that now require type parameters are listed below.
No type named TMap
client::TMap
has been removed. Use client::Map
(without the T
) instead.
client::TArray
still exists, and client::Array
is still type erased, for
now. This is likely to change in a future release.
Cannot initialize with value of type _Union<T...>*
Some functions that used to return a concrete type now return
client::_Union<T...>*
instead.
One example of this is the get_buffer
function on typed arrays. This
function used to return client::ArrayBuffer*
, but it now returns
client::_Union<client::ArrayBuffer*, client::SharedArrayBuffer*>*
.
To fix this, simply cast the union type using ->cast<T>()
. Depending on where
the result is being used, it may be possible to omit the type parameter to
cast
.
For example, for the above error, we could instead write:
No known conversion to ArrayLike<double>*
Functions that take ArrayLike<T>
used to be defined as template functions, so
they could take any type of parameter. These functions are no longer template
functions, they instead use the new client::ArrayLike<T>
template class.
client::TArray<T>
and typed arrays do not inherit from
client::ArrayLike<T>
.
One example of this is the set
function on typed arrays.
To pass an array to a function that expects client::ArrayLike<T>*
, you can
use ->cast<T>()
or ->cast()
.
Functions with different template parameters
Functions whose return type is not known at compile time were defined as template functions. Some of these functions are no longer template functions, and return a specific type instead.
One example of this is the client::JSON.parse
function. This function
now returns client::Object*
. The error you will get is slightly confusing.
To fix the error, simply remove the type parameter. Use ->cast<T>()
or
->cast()
if you need a type that is more specific than what the function
returns.
Redefinition of client class
The old Clientlib was missing some modern JavaScript types. To use these types,
you may have defined them yourself in the client
namespaces.
The new Clientlib already contains many of these modern types. Attempting to redefine them yourself results in an error.
To fix this, simply remove your definition of the duplicate class. The one in Clientlib should have all the methods you need.
Renamed classes
Some classes have been replaced with newer classes that have a different name.
For example, client::Element::getBoundingClientRect
now returns
client::DOMRect*
instead of client::ClientRect*
.
In this case, the error can be fixed by simply replacing client::ClientRect*
with client::DOMRect*
.
Ambiguous overloads
Some functions have added overloads that cause previously valid code to now be
ambiguous. This often happens when passing an integer to a function that has
overloads for both double
and some pointer type, or when a function has
overloads for const String&
and const _Union<T...>&
.
To fix the error, change the type of the argument to match the overload that you intend to call.
New features
_Any
, _Union
, and _Function
These are 3 new types to help improve the type safety of programs that interact with JavaScript code. The types do not exist in JavaScript, they only exist to help define accurate function signatures.
_Any
is similar to Object
, except that _Any
is also compatible with
primitive types, such as double
and int
. _Any
can be constructed on the
fly from any type. _Any
is roughly equivalent to any
in TypeScript.
_Union
is used to describe values whose type is unknown, but limited to a set
of known types. _Union
types can be constructed on the fly for any type in
the union. _Union
types can also be cast to a real type using ->cast()
or
->cast<T>()
. _Union<T...>
is roughly equivalent to T | ...
in TypeScript.
_Function
is a type safe first-class function type that replaces
EventListener*
when passing functions to higher order functions. _Function
can be constructed on the fly from raw function pointers and C++11 lambdas.
cheerp::Callback()
can still be used to get an EventListener*
, but prefer
constructing a _Function
instead.
_Union
and _Function
support many conversions to help keep your code clean.
For example, _Union<double>
can be converted to _Union<double, String*>
,
and _Function<void(Object*)>
can be converted to _Function<void(String*)>
.
->cast<T>()
and ->cast()
These functions have no effect other than to change the type of a value. They differ from other pointer casts in that they mimic the rules of casting objects in TypeScript, rather than in C++.
static_cast
has many restrictions that make in unsuitable for use with client
types. You cannot use it to cast through virtual base classes, which are used
to define some client types. static_cast
also cannot be used to perform many
casts involving _Union
and _Function
. Because, in the eyes of
static_cast
, _Union<String*>
and _Union<Object*>
are two completely
distinct and unrelated types. ->cast<T>()
and ->cast()
do allow you to
perform these casts.
->cast<T>()
does exactly what you’d expect, it returns a value of type T
.
->cast()
returns an intermediate object that implements a conversion operator
operator T();
. In many cases, the compiler can infer the type T
of the
conversion operator, and it doesn’t need to be specified explicitly.
Easing migration with -DUSE_OLD_CLIENTLIB
During migration, it can be useful to temporarily switch back to the old
Clientlib. This can be done by adding -DUSE_OLD_CLIENTLIB
to your compiler
flags. This flag only exists to ease migration to the new Clientlib. Do not
rely on this flag to build your project. It will be removed in a future
release.
Ask for help
If you have some code that cannot be migrated, or encounter any other issues with the new Clientlib, please reach out to us on Discord.
Join our Discord server