C++ language scalar function (remote mode)
This example uses the following file name:
applyopcpp.cpp
Code
The code in this example shows that, with minor changes, this program works in remote mode:
The change is in the main. The required remote AE connection name, testcapi, is set and then a loop is added around getAPI so it is called two times. This code works like a local AE and handles two iterations as a remote AE.
#include <nzaefactory.hpp>
using namespace nz::ae;
static int run(nz::ae::NzaeFunction *aeFunc);
int main(int argc, char * argv[])
{
NzaeApiGenerator helper;
// The following line is only needed if a launcher is not used
helper.setName("testcapi");
for (int i=0; i < 2; i++) {
nz::ae::NzaeApi &api = helper.getApi(nz::ae::NzaeApi::FUNCTION);
run(api.aeFunction);
if (!helper.isRemote())
break;
}
return 0;
}
class MyHandler : public NzaeFunctionMessageHandler
{
public:
MyHandler() {
m_Once = false;
}
enum OperatorEnum
{
OP_ADD = 1,
OP_MULT = 2
} op;
void doOnce(NzaeFunction& api) {
const NzaeMetadata& meta = api.getMetadata();
if (meta.getOutputColumnCount() != 1 ||
meta.getOutputType(0) != NzaeDataTypes::NZUDSUDX_DOUBLE) {
throw NzaeException("expecting one output column of type double");
}
if (meta.getInputColumnCount() < 1) {
throw NzaeException("expecting at least one input column");
}
if (meta.getInputType(0) != NzaeDataTypes::NZUDSUDX_FIXED &&
meta.getInputType(0) != NzaeDataTypes::NZUDSUDX_VARIABLE) {
throw NzaeException("first input column expected to be a string
type");
}
m_Once = true;
}
void evaluate(NzaeFunction& api, NzaeRecord &input, NzaeRecord &result) {
if (!m_Once)
doOnce(api);
double res = 0;
NzaeField &field = input.get(0);
if (field.isNull())
throw NzaeException("first input column may not be null");
NzaeStringField &sf = (NzaeStringField&) input.get(0);
std::string strop = (std::string)sf;
if (strop == "+")
op = OP_ADD;
else if (strop == "*") {
op = OP_MULT;
res = 1;
}
else
throw NzaeException(NzaeException::format("unexpected operator %s", \
strop.c_str()));
for (int i=1 ; i < input.numFields(); i++) {
NzaeField &inf = input.get(i);
if (inf.isNull())
continue;
double temp = 0;
bool ignore = false;
switch (inf.type()) {
case NzaeDataTypes::NZUDSUDX_INT8:
{
NzaeInt8Field &sf = (NzaeInt8Field&)input.get(i);
temp = (int8_t)sf;
break;
}
case NzaeDataTypes::NZUDSUDX_INT16:
{
NzaeInt16Field &sf = (NzaeInt16Field&)input.get(i);
temp = (int16_t)sf;
break;
}
case NzaeDataTypes::NZUDSUDX_INT32:
{
NzaeInt32Field &sf = (NzaeInt32Field&)input.get(i);
temp = (int32_t)sf;
break;
}
case NzaeDataTypes::NZUDSUDX_INT64:
{
NzaeInt64Field &sf = (NzaeInt64Field&)input.get(i);
temp = (int64_t)sf;
break;
}
case NzaeDataTypes::NZUDSUDX_FLOAT:
{
NzaeFloatField &sf = (NzaeFloatField&)input.get(i);
temp = (float)sf;
break;
}
case NzaeDataTypes::NZUDSUDX_DOUBLE:
{
NzaeDoubleField &sf = (NzaeDoubleField&)input.get(i);
temp = (double)sf;
break;
}
case NzaeDataTypes::NZUDSUDX_NUMERIC32:
case NzaeDataTypes::NZUDSUDX_NUMERIC64:
case NzaeDataTypes::NZUDSUDX_NUMERIC128:
{
NzaeNumericField &sf = (NzaeNumericField&)input.get(i);
temp = (double)sf;
break;
}
default:
ignore = true;
// ignore
break;
}
if (!ignore) {
if (op == OP_ADD)
res += temp;
else
res *= temp;
}
}
NzaeDoubleField &f = (NzaeDoubleField&)result.get(0);
f = res;
}
bool m_Once;
};
static int run(NzaeFunction *aeFunc)
{
aeFunc->run(new MyHandler());
return 0;
}
Compilation
Use the standard compile, as in local mode:
$NZ_EXPORT_DIR/ae/utilities/bin/compile_ae --language cpp --template compile \
--exe applyopcpp --compargs "-g -Wall" --linkargs "-g" applyopcpp.cpp \
--version 3
Registration
$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --sig "rem_applyop_cpp(VARARGS)" \
--return "double" --language cpp --template udf --exe applyopcpp \
--version 3 --remote --rname testcapi$NZ_EXPORT_DIR/ae/utilities/bin/register_ae --sig "rem_applyop_launch(int8)" \
--return "TABLE(aeresult varchar(255))" --language cpp --template udtf \
--exe applyopcpp --version 3 --remote --launch --rname testcapiWith the exception of the --rname, --exe and the name portion of --sig, all launchers look like the above. In this case it is a UDTF since that is the interface for all launchers. The value of --rname must match the name in the code.
Running
To run the AE in remote mode, the executable is run as a “server.” In this instance, it handles only queries run on the host. Usually, AEs are started on the SPUs as well. Start an instance on the host:
SELECT * FROM TABLE WITH FINAL(rem_applyop_launch(0));
AERESULT
-------------------------------------------------------------------------------
tran: 7192 DATA slc: 0 hardware: 0 machine: spubox1 process: 8306 thread: 8306
(1 row)
Notice that a different syntax is used to invoke the table function style launcher. This is the syntax used to call any UDTF-based AE. Now run the AE:
SELECT rem_applyop_cpp('+', 4,5,1.1);
REM_APPLYOP_CPP
-----------------
10.1
(1 row)
Finally, cause an error:
SELECT rem_applyop_cpp(1);
ERROR: first input COLUMN expected TO be a string type