I had always believed that knowledge flowed in one direction—from the old to the young, from teacher to student, from father to son. And for most of Ridit's childhood, that seemed true. I had introduced him to Java, C++, even helped him debug his first Python script. To me, guiding my son through the world of code was like watching seeds grow into a tree.
But trees have roots. And sometimes, the water flows up.
One sultry afternoon, I sat at my desk, thinking about a simple script engine especially made for C++. I have already tried with Python.Boost and SWIG. But I wanted to get something which is especially made for C++.
“I wish I could just write the logic in a script file and have the engine read it on the fly,” I muttered.
Ridit, my son, now in class IX, overheard me while lounging nearby with his laptop. He casually said, “Why don’t you try AngelScript?”
I looked up. “What’s that?”
“It’s a scripting language for games and simulations. Super fast, safe, and looks almost like C++. I used it to modify that open-source shooter I play—remember the one with voxel maps?”
I was intrigued but skeptical. “Never heard of it. You sure it can interface with my C++ code cleanly?”
Ridit opened his laptop and pulled up a project. “Here, look. You register your C++ objects like this, and then scripts can call them directly. No weird glue code. It's pretty smart.”
I leaned in, stunned at how elegant the integration looked. There it was—my solution, sitting quietly in my son's world this whole time.
By midnight, I had AngelScript embedded. My singleton Factory class was being called from a .as file, creating Biscuit and Chocolate objects like magic. All without a single recompile.
As we sat there, I finally spoke, eyes twinkling:
“You know, reverse osmosis usually means filtering the purest water against pressure. Today, I think the wisdom flowed backward—from son to father, against tradition—but damn, was it pure.”
Ridit grinned. “Guess I’m your tech support now.”
From that day, whenever I hit a wall, I'd first ask myself:
“What would my son Ridit do?”
So.... here we go...
The source code of the Factory Design Pattern in C++ and AngelScript...
/*
* Factory.h
*
* Created on: Mar 10, 2021
* Author: som
*/
#ifndef FACTORY_H_
#define FACTORY_H_
#include <iostream>
#include <string>
#include "Biscuit.h"
#include "Chocolate.h"
using namespace std;
class Factory{
public:
static Factory* instance;
static Factory* getInstance();
Food* makeFood(const string& type);
private:
Factory(){}
// Delete copy constructor & assignment operator (Singleton pattern)
Factory(const Factory&) = delete;
Factory& operator=(const Factory&) = delete;
};
//Factory* Factory:: instance = NULL;
#endif /* FACTORY_H_ */
/*
* Factory.cpp
*
* Created on: Jan 30, 2025
* Author: som
*/
#include "Factory.h"
Factory* Factory::instance = NULL;
Factory* Factory:: getInstance(){
if(Factory::instance == NULL){
Factory::instance = new Factory();
}
return Factory::instance;
}
Food* Factory::makeFood(const string& type){
if(type.compare("bi") == 0){
return new Biscuit();
}
if(type.compare("ch") == 0){
return new Chocolate();
}
std::cerr << "Unknown food type: " << type << std::endl;
return nullptr;
}
/*
* Food.h
*
* Created on: Mar 10, 2021
* Author: som
*/
#ifndef FOOD_H_
#define FOOD_H_
#include <string>
using namespace std;
class Food {
public:
virtual string getName() = 0;
virtual ~Food(){
}
};
#endif /* FOOD_H_ */
/*
* Biscuit.h
*
* Created on: Mar 10, 2021
* Author: som
*/
#ifndef BISCUIT_H_
#define BISCUIT_H_
#include "Food.h"
class Biscuit: public Food {
public:
Biscuit();
string getName();
~Biscuit();
};
#endif /* BISCUIT_H_ */
/*
* Biscuit.cpp
*
* Created on: Mar 10, 2021
* Author: som
*/
#include <iostream>
#include "Biscuit.h"
using namespace std;
Biscuit::Biscuit() {
// TODO Auto-generated constructor stub
cout<<"Biscuit is made..."<<endl;
}
Biscuit::~Biscuit(){}
string Biscuit::getName(){
return "It's a Biscuit";
}
/*
* Chocolate.h
*
* Created on: Mar 10, 2021
* Author: som
*/
#ifndef CHOCOLATE_H_
#define CHOCOLATE_H_
#include <iostream>
#include "Food.h"
class Chocolate: public Food {
public:
Chocolate();
virtual ~Chocolate();
string getName();
};
#endif /* CHOCOLATE_H_ */
/*
* Chocolate.cpp
*
* Created on: Mar 10, 2021
* Author: som
*/
#include "Chocolate.h"
Chocolate::Chocolate() {
// TODO Auto-generated constructor stub
cout<<"Chocolate is made..."<<endl;
}
Chocolate::~Chocolate() {
// TODO Auto-generated destructor stub
}
string Chocolate::getName(){
return "It's a Chocolate";
}
Test.as
void main() {
Factory@ factory = getFactory();
if (factory is null) {
print("Factory is null!");
return;
}
Food@ item1 = factory.makeFood("ch");
if (item1 is null) {
print("item1 is null!");
} else {
print(item1.getName());
}
Food@ item2 = factory.makeFood("bi");
if (item2 is null) {
print("item2 is null!");
} else {
print(item2.getName());
}
}
//============================================================================
// Name : FactoryPattern.cpp
// Author : Som
// Version :
// Copyright : som-itsolutions
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <iostream>
#include <angelscript.h>
#include <scriptstdstring.h>
#include <scriptbuilder.h>
#include "Factory.h"
using namespace std;
void print(const std::string &msg) {
std::cout << msg << std::endl;
}
int RegisterWithAngelScript(asIScriptEngine* engine) {
// Register std::string
//RegisterStdString(engine);
// Register base class
int r;
RegisterStdString(engine);
r = engine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(print), asCALL_CDECL);
if (r < 0) { std::cerr << "Failed to register print()\n"; return -1; }
r = engine->RegisterObjectType("Food", 0, asOBJ_REF | asOBJ_NOCOUNT);
if (r < 0) { std::cerr << "Failed to register Food\n"; return -1; }
engine->RegisterObjectMethod("Food", "string getName()", asMETHOD(Food, getName), asCALL_THISCALL);
// Register Factory
engine->RegisterObjectType("Factory", 0, asOBJ_REF | asOBJ_NOCOUNT);
engine->RegisterGlobalFunction("Factory@ getFactory()", asFUNCTION(Factory::getInstance), asCALL_CDECL);
engine->RegisterObjectMethod("Factory", "Food@ makeFood(const string &in)", asMETHOD(Factory, makeFood), asCALL_THISCALL);
return 0;
}
int main() {
//int r;
asIScriptEngine* engine = asCreateScriptEngine();
if (!engine) {
std::cerr << "Failed to create script engine" << std::endl;
return -1;
}
//RegisterStdString(engine);
int ret = RegisterWithAngelScript(engine);
if (ret <0 ){
std::cerr << "Failed to register" << std::endl;
return -1;
}
CScriptBuilder builder;
builder.StartNewModule(engine, "MyModule");
builder.AddSectionFromFile("src/test.as");
int retValofBuilder = builder.BuildModule();
if (retValofBuilder < 0) {
std::cerr << "Script build failed!" << std::endl;
return -1;
}
asIScriptModule *mod = engine->GetModule("MyModule");
asIScriptFunction *func = mod->GetFunctionByDecl("void main()");
asIScriptContext *ctx = engine->CreateContext();
ctx->Prepare(func);
int r = ctx->Execute();
if (r != asEXECUTION_FINISHED) {
if (r == asEXECUTION_EXCEPTION) {
std::cerr << "⚠️ Script Exception: " << ctx->GetExceptionString() << std::endl;
const asIScriptFunction *func = ctx->GetExceptionFunction();
std::cerr << "⚙️ Function: " << func->GetDeclaration() << std::endl;
std::cerr << "📍 Line: " << ctx->GetExceptionLineNumber() << std::endl;
} else if (r == asEXECUTION_ABORTED) {
std::cerr << "❌ Script was aborted." << std::endl;
} else if (r == asEXECUTION_SUSPENDED) {
std::cerr << "⏸ Script was suspended." << std::endl;
} else {
std::cerr << "❌ Script execution failed with code: " << r << std::endl;
}
}
ctx->Release();
engine->ShutDownAndRelease();
return 0;
}