Scope. Namespaces
Scope#
Scope is a region of a program that determines accessibility of an identifier (whether a variable, enum, function, class, namespace and so on) that is used in this program.
How large the scope is depends on where the identifier is declared. For example, if a variable is declared at the top of a class then it will be accessible to all of the class methods. If it’s declared in a method then it can only be used in that method.
A pair of curly braces ({}) defines a new scope.
For example:
namespace Foo {
int a = 10;
}
int a = 5;
log.message("Foo::a is %d, a is %d", Foo::a,a);
Foo::a is 10, a is 5
If an identifier is declared outside all blocks enclosed by curly braces, it will have the global scope. It means that such identifier will be accessible anywhere in the program after its declaration.
// function foo() is in the global scope
int foo() { log.message("Function in the global scope\n"); }
class Foo {
Foo (){}
// function foo() is in the scope of class Foo
int foo(){ log.message("Class member function\n"); }
};
As it was mentioned above, lifetime of a variable is not limited by a current scope. In other words, lifetime of local variables is the same as lifetime of global ones (unless otherwise specified). For example:
int foo() {
int local_var;
local_var++;
log.message("local_var: %d\n",local_var);
}
foo(); // local_var: 1
foo(); // local_var: 2
If you want to emulate local lifetime of the local_var variable, it should be initialized as follows:
int foo() {
int local_var = 0;
local_var++;
log.message("local_var: %d\n",local_var);
}
foo(); // local_var: 1
foo(); // local_var: 1
Scope Resolution Operator#
The scope resolution operator :: is used to specify the context to which an identifier refers. It can be used to access a variable outside a namespace, to access a function outside a class or to resolve the scope of an identifier which is used to represent at the same time, for example, a global variable and a class member.
By prefixing a name of a function (variable, class, namespace and so on) with ::, you specify that the function from the global scope must be used. This ensures that scope resolution will start from the global scope instead of the current one. For example:
namespace Test {
int a;
void info() { log.message("a from the scope of the 1st namespace is %d\n",a); }
}
int init () {
namespace Test {
int a;
void info() { log.message("a from the scope of the 2nd namespace is %d\n",a); }
}
// access to the members of the global namespace
::Test::a = 10;
::Test::info();
// access to the members of the namespace declared in the current scope
Test::a = 30;
Test::info();
return 1;
}
a from the scope of the 1st namespace is 10
a from the scope of the 2nd namespace is 30
Supposing, there is a user-defined class with a function that has the same name as a function of the UnigineScript library. To call the original UnigineScript function inside the scope where the user function is defined, add the :: operator as the prefix to its name. For example:
class Foo {
Foo() {}
// user-defined function in the scope of the Foo class
void rotate(float angle) {
// UnigineScript function used to get a matrix of rotation at the given angle around the axis
mat4 rot = ::rotate(vec3(1.0f, 0.0f, 0.0f), angle);
}
};
NameSpace::getFunctionID(): can't find "rotate" function with 2 arguments
Namespaces#
Namespaces allow to group entities like classes, objects and functions under a name. This way the global scope can be divided in "sub-scopes", each one with its own name.
The format of namespaces is:
namespace identifier {
// entities
}
The example of namespace defining:
namespace Foo {
int a = 10;
}
int a = 5;
log.message("Foo::a is %d, a is %d", Foo::a,a);
// Output: Foo::a is 10, a is 5
It is also possible to use using keyword to introduce a name from a namespace into the current declarative region.
namespace Foo::Bar {
int a = 10;
}
void foo() {
using Foo::Bar;
log.message("Foo::Bar::a is %d\n",a);
}
foo();
// Output: Foo::Bar::a is 10