2023-07-04 04:00 AM - last edited on 2024-09-26 12:23 PM by Doreena Deng
Been working with the latest API with the intent to make available some of the features of the API examples to Python scripts. My idea is that Python is much easier to work with and doesn’t require a recompilation anytime a change is made. Very easy to experiment with. I also don’t want to reinvent the wheel since most of the functions of interest are already contained within the examples. Unfortunately, they are not in a form which can be immediately used, requiring one to recreate and compile the AddOn in a form more specific to one’s needs. It is much, much easier to experiment with a Python script. However, the present Python API has almost no access to the functions demoed in the C++ API examples. And, Graphisoft has indicated that they have no intention of upgrading the Python API in the near future.
This why I would like to create parallel AddOn examples which would make those functions available. Actually, this is relatively straight forward, by just making the Graphisoft menu items available to a Python script and passing over the results.
However, there is a problem. Graphisoft’s examples almost exclusively write their data/results to the session report via a variadic template (WriteReport). Completely worthless!! The data (output) is packed in a variadic argument and I am unable to unpack it or know how.
I tried using stdargs.h but just get a error on va_start of “va_start intrinsic only allowed in varags”. I also asked Graphisoft’s beta tech support to provide a solution but was just ignored. Understandable, since the beta site now has over 250 submittals. They are swamped.
Can anyone provide a means to unpack and use a variadic argument within a template which would provide access to all the results.
template<typename... Args>
void WriteReport (const char* format, Args&&... args)
{
ACAPI_WriteReport (format, false, std::forward<Args> (args)...);
}
Of course, I could just bypass the WriteReport function(s), but that would be a huge amount of work – not practical.
Can anyone help. Help me, help you?
2023-07-04 10:25 PM - edited 2023-07-04 11:14 PM
Hi Gerry,
If I understand you correctly, you want to replace WriteReport with your own implementation right?
The easiest way I've found to unpack the arguments is recursion.
I think something like the following should do what you want: (Live Demo on Godbolt)
// Adapted from:
// https://learn.microsoft.com/en-us/cpp/cpp/ellipses-and-variadic-templates?view=msvc-170
#include <iostream>
using namespace std;
void print() {
cout << endl;
}
template <typename T> void print(const T& t) {
cout << t << endl;
}
template <typename First, typename... Rest> void print(const First& first, const Rest&... rest){
cout << first << ", ";
print(rest...); // recursive call using pack expansion syntax
}
// WriteReport replacement to substitute functionality and ignore first 'format' parameter.
template<typename... Args>
void WriteReport (const char* /*format*/, Args&&... args)
{
print (std::forward<Args> (args)...);
}
int main()
{
WriteReport("this is ignored", 1, 3, 4, 5);
WriteReport("this is ignored", 1.23, "some string");
}
So in this example, you can change print to do different things of course. The key is the recursive implementation of it.
Hope this helps!
Bernd
2023-07-05 06:19 AM - edited 2023-07-05 06:23 AM
Typed in the above but get a compile error:
binary '<<': no operator found which takes a right hand operand of type 'const First' (or there is no acceptable conversion)
and
binary '<<' : no operator found which takes a right-hand operand of type 'const T' (or there is no acceptable conversion)
using VS Studio 2019 on windows 10
2023-07-05 09:57 PM
The error messages should continue with something like:
.\main.cpp(10): error C2679: binary '<<': no operator found which takes a right-hand operand of type 'const T' (or there is no acceptable conversion)
with
[
T=MissingType
]
This means you are calling the WriteReport (which calls print which calls operator<<) function with a type/class which doesn't have an '<<' operator defined.
So it depends on what you want to use inside the print function.
Probably easiest is to provide template specializations of the print function for all missing types.
So add functions like this to the code:
void print(const MissingType& value) {
cout << CONVERT_VALUE_TO_SOMETHING_PRINTABLE_HERE(value) << endl;
// maybe GS::ValueToUniString(value) works
}
2023-07-05 10:58 PM
Thanks for the prompt reply -- I really appreciate it
But it still won't compile. I don't understand where to place the code
For example: what do I put in for"missingType":
Do I add the function into a Template? what template?
i believe WriteRport is converting the UniString to char first -- could be wrong?
Could you provide the full code? i am really lost with templates?
2023-07-06 10:41 AM
I understand that it's hard to wrap your head around templates at first and I don't really know how to explain it concisely.
@poco2013 wrote:
Could you provide the full code?
To provide a full code, I first would have to now what types you are using.
Can you send me the code you are working on somehow? PM, GitHub, GitLab or Mail (check my website for the contact)
@poco2013 wrote:
For example: what do I put in for"missingType":
You'd need to make a separate print function for each MissingType, where MissingType is given in the error messages:
.\main.cpp(10): error C2679: binary '<<': no operator found which takes a right-hand operand of type 'const T' (or there is no acceptable conversion)
with
[
T=MissingType
]
2023-07-06 02:30 PM
Here are typical Archicad statements that I am trying to convert:
WriteReport ("# List attributes:");
WriteReport ("# - scan all attribute types and show the number of each type");
WriteReport ("# - deleted instances will be called \"DEL\"");
WriteReport ("%s: %d", AttrID_To_Name (typeID), attributes.GetSize ());
char guidStr[64];
APIGuid2GSGuid (attrib.header.guid).ConvertToString (guidStr);
WriteReport (" [%3s] {%s} \"%s\"", attrib.header.index.ToUniString ().ToCStr ().Get (), guidStr, attrib.header.name);
Archicad Copies the arguments in WriteReport to its function ACAPI_WriteReport with a variadic template in the file APICommon.h
template<typename... Args>
void WriteReport (const char* /*format*/, Args&&... args)
{
ACAPI_WriteReport (format, false, std::forward<Args> (args)...);
}.
Their WriteReport writes to session info, note the false argument, which is worthless. What I want to do is to remove ACAPI_WriteReport and inside the template, and write the variadic argument to a Array so that they can be transferred to another program (Python), I could bypass WriteReport directly, but it is used in hundreds of places in each AddOn -- so not practical. Basically I am trying to stop writing to the session report and capture the data directly.
I am still playing with your suggestions. Thanks for your previous help. I think this is just a mater of understanding what the compiler expects, but can't find any documentation. Most is either on the elementary level or too complicated to follow. However, i appreciate your offers to help- I am just a little slow.
2023-07-06 04:52 PM - edited 2023-07-06 04:53 PM
In your example there are only the following types given to WriteReport:
So I've simplified your example to pass such values directly instead of grabbing them from attributes etc.
If you are passing other types, my example code might not work and you'll need to provide more template specializations!
So please check first if you can run it just with the example WriteReport lines you've given.
We then can add further specializations if you provide me with the failing types.
I've chosen GS::UniString as the storage type for now since I don't know how you could make a storage which takes all the possible types.
This would be considerably more work if at all possible.
Here's the example code. Call the function TestAddingToArrayIfWriteReportIsCalled () somehow within an Add-On.
class WRArray {
public:
GS::Array<GS::UniString> container;
static WRArray& WRArray::GetInstance ()
{
static WRArray instance;
return instance;
}
void addToArray () {
return;
}
template <typename T>
void addToArray (const T tValue) {
container.Push (GS::ValueToUniString (tValue));
}
template <typename First, typename... Rest> void addToArray (const First& first, const Rest&... rest) {
addToArray (first);
addToArray (rest...); // recursive call using pack expansion syntax
}
// Template specializations here!
// Anything that doesn't support GS::ValueToUniString will need to be explicitly implemented similar to this:
template<>
void addToArray<> (const char* cStr) {
container.Push (GS::UniString (cStr));
}
};
template<typename... Args>
void WriteReport (const char* /*format*/, Args&&... args)
{
//ACAPI_WriteReport (format, false, std::forward<Args> (args)...);
WRArray::GetInstance ().addToArray (std::forward<Args> (args)...);
}
void TestAddingToArrayIfWriteReportIsCalled ()
{
// These don't add anything to the array since we are ignoring the format string
WriteReport ("# List attributes:");
WriteReport ("# - scan all attribute types and show the number of each type");
WriteReport ("# - deleted instances will be called \"DEL\"");
// Format string will be ignored. First entry in array will be "API_PenID"
WriteReport ("%s: %d", "API_PenID", GS::USize(32));
char guidStr[64] = "{000000-0000-0000-000000}";
WriteReport (" [%3s] {%s} \"%s\"", "some c string", guidStr, "PEN1");
for (const GS::UniString& item : WRArray::GetInstance ().container) {
// Do something else here, just demonstrating that we have the values in an array now.
ACAPI_WriteReport (item, true);
}
}
2023-07-06 08:39 PM
OK Were sneaking up on it. The class compiled OK. But as soon as i add the line
WRArray::GetInstance().addToArray(std::forward<Args>(args)...);
to the template<typename... Args>
i get the compile error of
C2665 GS::valuetostr: none of the 29 overloads could convert all the argument types
Still working on it.
2023-07-11 09:18 AM
You said, that you are using the C++ API Example projects. Which one are you looking at exactly?
Because I've just tried with the Element_Test example from the AC26 DevKit.
I replaced WriteReport with the class + new implementation of the function in the APICommon.h file of that project and I don't get any errors like you've said.
Unfortunately the code doesn't work directly though. Somehow the array I defined is not as global as I've thought 😅
So that part would need some work still as well.