Skip to content

Commit

Permalink
autoSTRVECToArray fix candidate #2
Browse files Browse the repository at this point in the history
  • Loading branch information
hokiedsp committed Jul 4, 2024
1 parent 783628b commit 613888e
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 48 deletions.
107 changes: 61 additions & 46 deletions src/parselmouth/praat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,23 @@ py::object autoSTRVECToArray(autoSTRVEC &&vector) {
if (!vector.elements)
return py::none();


auto v = vector.get(); // _stringvector <char32>
// return py::array_t<py::object>(py::cast(std::vector<char32 *>(v.begin(), v.end()))); // fails with error: static assertion failed: Attempt to use a non-POD or unimplemented POD type as a numpy dtype

// candidate #1: still the same error if casted,
// std::vector<std::u32string_view> str_vec;
// std::transform(v.begin(), v.end(), std::back_inserter(str_vec), [](auto &s)
// { return std::u32string_view(s,std::char_traits<char32>::length(s)); });
// return str_vec;

py::list str_list(v.size);
for (int i=0;i<v.size;++i)
str_list[i] = v[i+1];
return py::array(str_list);

// original - fails with error: static assertion failed: Attempt to use a non-POD or unimplemented POD type as a numpy dtype
// return py::array_t<py::object>(py::cast(std::vector<char32 *>(v.begin(), v.end())));

// candidate 1 - py::list->py::array - seems redundant
// py::list str_list(v.size);
// for (int i=0;i<v.size;++i)
// str_list[i] = v[i+1];
// return py::array(str_list);

// candidate 2 - most comparable to the original?
py::array str_array(py::dtype('O'), {v.size}, {});
auto view = static_cast<py::object *>(str_array.mutable_data());
for (int i = 0; i < v.size; ++i)
view[i] = py::cast(v[i + 1]);
return str_array;
}

class PraatEnvironment {
Expand Down Expand Up @@ -224,12 +227,31 @@ class PraatEnvironment {
};

// Workarounds since GCC (6) doesn't seem to like the brace initialization of the nested anonymous struct
void fillStackel(structStackel &s, double number) { s.which = Stackel_NUMBER; s.number = number; }
void fillStackel(structStackel &s, bool boolean) { s.which = Stackel_NUMBER; s.number = boolean; }
void fillStackel(structStackel &s, const std::u32string &string) { s.which = Stackel_STRING; s._string = Melder_dup(string.c_str()); }
void fillStackel(structStackel &s, const VEC &vector, bool owned) { s.which = Stackel_NUMERIC_VECTOR; s.owned = owned; s.numericVector = vector; }
void fillStackel(structStackel &s, const MAT &matrix, bool owned) { s.which = Stackel_NUMERIC_MATRIX, s.owned = owned; s.numericMatrix = matrix; }
void fillStackel(structStackel &s, const STRVEC &array, bool owned) { s.which = Stackel_STRING_ARRAY, s.owned = owned; s.stringArray = array; }
void fillStackel(structStackel &s, double number) {
s.which = Stackel_NUMBER;
s.number = number;
}
void fillStackel(structStackel &s, bool boolean) {
s.which = Stackel_NUMBER;
s.number = boolean;
}
void fillStackel(structStackel &s, const std::u32string &string) {
s.which = Stackel_STRING;
s._string = Melder_dup(string.c_str());
}
void fillStackel(structStackel &s, const VEC &vector, bool owned) {
s.which = Stackel_NUMERIC_VECTOR;
s.owned = owned;
s.numericVector = vector;
}
void fillStackel(structStackel &s, const MAT &matrix, bool owned) {
s.which = Stackel_NUMERIC_MATRIX, s.owned = owned;
s.numericMatrix = matrix;
}
void fillStackel(structStackel &s, const STRVEC &array, bool owned) {
s.which = Stackel_STRING_ARRAY, s.owned = owned;
s.stringArray = array;
}

void PraatEnvironment::toPraatArg(structStackel &stackel, const py::handle &arg) {
if (py::isinstance<py::int_>(arg) || py::isinstance<py::float_>(arg))
Expand Down Expand Up @@ -263,8 +285,10 @@ void PraatEnvironment::toPraatArg(structStackel &stackel, const py::handle &arg)
throw py::value_error("Cannot convert " + std::to_string(array.ndim()) + "-dimensional NumPy array argument\"" + py::cast<std::string>(py::repr(arg)) + "\" to a Praat vector or matrix");
}
}
catch (py::cast_error &) {}
catch (py::error_already_set &) {}
catch (py::cast_error &) {
}
catch (py::error_already_set &) {
}

try {
auto list = py::cast<py::list>(arg);
Expand All @@ -274,8 +298,10 @@ void PraatEnvironment::toPraatArg(structStackel &stackel, const py::handle &arg)
}
return fillStackel(stackel, array.releaseToAmbiguousOwner(), true);
}
catch (py::cast_error &) {}
catch (py::error_already_set &) {}
catch (py::cast_error &) {
}
catch (py::error_already_set &) {
}

throw py::value_error("Cannot convert argument \"" + py::cast<std::string>(py::repr(arg)) + "\" to a known Praat argument type");
}
Expand Down Expand Up @@ -306,7 +332,7 @@ py::object PraatEnvironment::fromPraatResult(const std::u32string &callbackName,
case kInterpreter_ReturnType::VOID_:
return py::none();
case kInterpreter_ReturnType::OBJECT_:
return returnObjects(callbackName); // TODO Improve, without relying on the callback name?
return returnObjects(callbackName); // TODO Improve, without relying on the callback name?
case kInterpreter_ReturnType::REAL_:
return py::cast(Melder_atof(interceptedInfo.c_str()));
case kInterpreter_ReturnType::INTEGER_:
Expand Down Expand Up @@ -384,7 +410,7 @@ auto runPraatScript(const std::vector<std::reference_wrapper<structData>> &objec
try {
Interpreter_readParameters(environment.interpreter(), script);
Interpreter_getArgumentsFromArgs(environment.interpreter(), static_cast<int>(praatArgs.size() - 1), praatArgs.data());
Interpreter_run(environment.interpreter(), script, false); // TODO: Is reuseVariables useful for us?
Interpreter_run(environment.interpreter(), script, false); // TODO: Is reuseVariables useful for us?
}
catch (MelderError) {
Melder_throw(U"Script not completed.");
Expand Down Expand Up @@ -430,9 +456,10 @@ auto runPraatScriptFromFile(const std::vector<std::reference_wrapper<structData>
}

auto castPraatCommand(const structPraat_Command &command) {
// Don't blame me, blame Praat using class1, class2, ... instead of a list
#define CAST_CLASS(N) command.class##N->className, command.n##N
#define MAYBE_ADD_CLASS(N) if (command.class##N) classes.emplace_back(CAST_CLASS(N))
// Don't blame me, blame Praat using class1, class2, ... instead of a list
#define CAST_CLASS(N) command.class##N->className, command.n##N
#define MAYBE_ADD_CLASS(N) \
if (command.class##N) classes.emplace_back(CAST_CLASS(N))

std::vector<decltype(std::tuple(CAST_CLASS(1)))> classes;
MAYBE_ADD_CLASS(1);
Expand All @@ -450,39 +477,27 @@ using CastedPraatCommand = decltype(castPraatCommand(std::declval<structPraat_Co
class PraatModule;

PRAAT_MODULE_BINDING(praat, PraatModule, PRAAT_MODULE_DOCSTRING) {
def("call",
[](const std::u32string &command, py::args args, py::kwargs kwargs) { return callPraatCommand({}, command, args, kwargs); },
"command"_a);
def("call", [](const std::u32string &command, py::args args, py::kwargs kwargs) { return callPraatCommand({}, command, args, kwargs); }, "command"_a);

def("call",
[](structData &data, const std::u32string &command, py::args args, py::kwargs kwargs) { return callPraatCommand({ std::ref(data) }, command, args, kwargs); },
"object"_a, "command"_a);
def("call", [](structData &data, const std::u32string &command, py::args args, py::kwargs kwargs) { return callPraatCommand({std::ref(data)}, command, args, kwargs); }, "object"_a, "command"_a);

def("call",
&callPraatCommand,
"objects"_a, "command"_a,
PRAAT_CALL_DOCSTRING);

def("run",
[](const std::u32string &script, py::args args, py::kwargs kwargs) { return runPraatScriptFromText({}, script, args, kwargs); },
"script"_a);
def("run", [](const std::u32string &script, py::args args, py::kwargs kwargs) { return runPraatScriptFromText({}, script, args, kwargs); }, "script"_a);

def("run",
[](structData &data, const std::u32string &script, py::args args, py::kwargs kwargs) { return runPraatScriptFromText({ std::ref(data) }, script, args, kwargs); },
"object"_a, "script"_a);
def("run", [](structData &data, const std::u32string &script, py::args args, py::kwargs kwargs) { return runPraatScriptFromText({std::ref(data)}, script, args, kwargs); }, "object"_a, "script"_a);

def("run",
&runPraatScriptFromText,
"objects"_a, "script"_a,
PRAAT_RUN_DOCSTRING);

def("run_file",
[](const std::u32string &path, py::args args, py::kwargs kwargs) { return runPraatScriptFromFile({}, path, args, kwargs); },
"path"_a);
def("run_file", [](const std::u32string &path, py::args args, py::kwargs kwargs) { return runPraatScriptFromFile({}, path, args, kwargs); }, "path"_a);

def("run_file",
[](structData &data, const std::u32string &path, py::args args, py::kwargs kwargs) { return runPraatScriptFromFile({ std::ref(data) }, path, args, kwargs); },
"object"_a, "path"_a);
def("run_file", [](structData &data, const std::u32string &path, py::args args, py::kwargs kwargs) { return runPraatScriptFromFile({std::ref(data)}, path, args, kwargs); }, "object"_a, "path"_a);

def("run_file",
&runPraatScriptFromFile,
Expand Down
4 changes: 2 additions & 2 deletions tests/test_praat.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ def test_call_return_string_vector():
strings_vector = parselmouth.praat.call(strings, "List all strings")
assert isinstance(strings_vector, np.ndarray)
assert strings_vector.shape == (5,)
# assert strings_vector.dtype == np.object_
assert strings_vector.dtype == np.dtype('<U5')
assert strings_vector.dtype == np.object_
# assert strings_vector.dtype == np.dtype('<U5') # <- for candidate 1
assert np.all(strings_vector == sentence.split(" "))


Expand Down

0 comments on commit 613888e

Please sign in to comment.