Archicad C++ API
About Archicad add-on development using the C++ API.
SOLVED!

Attributes selected by name - char and pointers

Anonymous
Not applicable
Hi,

I'm trying to select attributes by name. I based it on Example I and II in AttributeGet docs.
After a while, I had Example 2 working but... It's doing well for all strings which have same beginning. So T, TE, TES, TEST all of them work. I assume its char handling but I'm not sure how to make it work.
Instead of the header.name I tried UniString pointer but referencing causes AC crashed.
Here is my try:
/* Example II -- loop through all attributes of a kind */
API_Attribute  attrib;
	short          nLin, i;
	GSErrCode      err;

	BNZeroMemory(&attrib, sizeof(API_Attribute));
	attrib.header.typeID = API_CompWallID;
	err = ACAPI_Attribute_GetNum(API_CompWallID, &nLin);
	
	for (i = 1; i <= nLin && err == NoError; i++) {
		attrib.header.index = i;
		err = ACAPI_Attribute_Get(&attrib);

		if (err == NoError) {
			char TTT[100] = "TEST";
			int TTT_Idx;
			
			char AttTxt = *attrib.compWall.head.name;

			if (AttTxt == *TTT) {
				DGAlert(DG_INFORMATION, "ERR1", "ATTRIB GUT", "", "Good");
				TTT_Idx = attrib.compWall.head.index;
			}
Example 1 didn't work at all. I tried building header but search ends up with an error.
	//Example I
	API_Attribute attrib;
	BNZeroMemory(&attrib, sizeof(API_Attribute));
	char TTT[256] = "TEST";

	attrib.header.name[256] = *TTT;
	
	err = ACAPI_Attribute_Search(&attrib.header);

	if (err == NoError) {
	char msgStr[512];
	sprintf(msgStr, "[%d] %s", attrib.header.index, attrib.header.name);
	ACAPI_WriteReport(msgStr, true);
	DGAlert(DG_INFORMATION, "ERR1",  msgStr, "", "Good");
	}
	else {
	DGAlert(DG_INFORMATION, "ERR1", "error", "", "Good");
	}
Please help
1 ACCEPTED SOLUTION

Accepted Solutions
Solution
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
You handled the character arrays incorrectly.

In "Example II" you made the mistake here:
kzaremba wrote:
/* Example II -- loop through all attributes of a kind */
...
			char AttTxt = *attrib.compWall.head.name;

			if (AttTxt == *TTT) {
...
This way you copied only the first character of the attrib.compWall.head.name string to the AttTxt variable and in the if statement you compared only the first character of TTT to AttTxt.

This would be correct:
/* Example II -- loop through all attributes of a kind */
...
			if (strcmp (attrib.compWall.head.name, TTT) == 0) {
...
You don't need the AttTxt variable and you can compare (char*) strings using strcmp method.

But to search attributes with a specific name the ACAPI_Attribute_Search function is recommended, so your other example code fits better for this purpose.
You made mistake in your "Example I" here:
kzaremba wrote:
	//Example I
...
	char TTT[256] = "TEST";

	attrib.header.name[256] = *TTT;
...
This way only the first character from TTT will be copied.

Use strcpy method to copy the whole string:
	//Example I
...
strcpy (attrib.header.name, "TEST");
...

View solution in original post

6 REPLIES 6
Solution
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
You handled the character arrays incorrectly.

In "Example II" you made the mistake here:
kzaremba wrote:
/* Example II -- loop through all attributes of a kind */
...
			char AttTxt = *attrib.compWall.head.name;

			if (AttTxt == *TTT) {
...
This way you copied only the first character of the attrib.compWall.head.name string to the AttTxt variable and in the if statement you compared only the first character of TTT to AttTxt.

This would be correct:
/* Example II -- loop through all attributes of a kind */
...
			if (strcmp (attrib.compWall.head.name, TTT) == 0) {
...
You don't need the AttTxt variable and you can compare (char*) strings using strcmp method.

But to search attributes with a specific name the ACAPI_Attribute_Search function is recommended, so your other example code fits better for this purpose.
You made mistake in your "Example I" here:
kzaremba wrote:
	//Example I
...
	char TTT[256] = "TEST";

	attrib.header.name[256] = *TTT;
...
This way only the first character from TTT will be copied.

Use strcpy method to copy the whole string:
	//Example I
...
strcpy (attrib.header.name, "TEST");
...
Anonymous
Not applicable
Hi Tibor,

Thanks a lot, I have struggled with chars for a while. Example I works as a charm!

I couldn't get Example II working. strcmp gets violet in VS and got this description "strcmp_DISABLED!!!" and compiler error C2065.
dfintha
Graphisoft Alumni
Graphisoft Alumni
Hello,

Yes, strcmp is disabled in add-on sources. You can use the CHCompareCStrings function instead, which has a similar set of arguments, and produces identical results.

Best regards,
Dénes
Anonymous
Not applicable
Hi, thanks a lot for pointing it out.
Right now I struggle a lot with char/std::string/GS::String conversions. What is the best practice in your opinion to handle chars and string? Using standard lib or GSRoot? Does it make any difference?
dfintha
Graphisoft Alumni
Graphisoft Alumni
Hello,

Using GS::UniString is the preferred method, as it is used in the API function calls and structures, and it uses UTF-8 encoding.
Also, you can use it in a similar fashion as you would use the std::string class, and you can use the GS::UniString::Printf static function instead of sprintf to construct a GS::UniString instance.

If you need a zero-terminated C string, you can convert a GS::UniString to a const char * by using the ToCStr method.


GS::UniString myStr = "Hello, world!";
// ...
FunctionTakingCStr (myStr.ToCStr ().Get ());

Best regards,
Dénes
Anonymous
Not applicable
To be honest, I don't need to use zero-terminated.. but AC seems to need them sometimes. Now I know how to handle them thanks a lot