Go Down

Topic: error: conversion from 'MyClass::MyStruct' to non-scalar type 'MyStruct' request (Read 201 times) previous topic - next topic

Stephane_63

Hi,

I have this error :
Quote
sketch_mar20a.ino: In function 'void loop()':
sketch_mar20a.ino:18:30: error: conversion from 'MyClass::MyStruct' to non-scalar type 'MyStruct' requested
Erreur lors de la compilation.
in the ino code with a struct, using a class using the same struct (I use Copy / Paste to be sure).

(Arduino Leonardo + Win 7)

I put here a light version of the codes of the ino, the .h and .cpp classes.

Code: (Arduino) [Select]

#include <SoftwareSerial.h>
#include <MyClass.h>

MyClass C(10, 11); //rx et tx

struct MyStruct {
  bool MyBool;
  String MyString;
};

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:
  MyStruct S = C.GetMyStruct(); //The error is in this line

}



Code: (header) [Select]

#pragma once
#include <Arduino.h>
#include <SoftwareSerial.h>
class MyClass
{
public:
MyClass(int rx, int tx);
~MyClass();

struct MyStruct {
bool MyBool;
String MyString;
};

MyStruct GetMyStruct();

private:
SoftwareSerial *MySerial;

};

Code: (cpp) [Select]

#include "MyClass.h"

MyClass::MyClass(int rx, int tx)
{
MySerial = new SoftwareSerial(rx, tx);
}

MyClass::~MyClass()
{
}
MyStruct MyClass::GetMyStruct()
{
MyStruct S;
S.MyBool = false;
S.MyString = "";
//Code
//...
return S;
}


Thanks for your help,
St├ęphane
French user (and new in CPP and in Arduino)

Peter_n

Hi, welcome to the forum.
You really jumped into the deep, using both struct and class.

Can you tell what you want to achieve ?
I'm confusing how the data (and memory locations) are used.
The class should spit out some data as a struct ?

This is okay: int i = 3;
This is not okay : myStruct S = someFunction();
The '=' is not an operator for a struct. You could use a pointer, or memcpy().

You use MyStruct as a normal struct definition, but also a MyStruct inside the class. That are two different things with the same name. No wonder the compiler gets confused. Maybe it is okay for the compiler, but it confuses me ;)

The MyClass::GetMyStruct() creates a MyStruct S on the stack. That data (on the stack) is returned. But outside the function it is out of scope.

I think something like this will work:
Code: [Select]

MyClass::GetMyStruct(MyStruct *pS)
{
  pS->
  ...

MyStruct S;
GetMyStruct(&S);


But I'm not sure if that is what you need. Perhaps there is a simpler way to do what you want to achieve.

Stephane_63

Thank you Peter for your help.

I tryed to use a pointer too, but it didn't work anymore.

Why I want a struc in my class ?
Because, my function read the serial and return a string if data was in the buffer (ok = true in this case and data = the buffer), and if there is no data available, ok = false (and data ="").
Ok, I know I can only test data = "" to know the equivalent of ok = false, and then the struc is useless, but it is possible in other functions I have to return 2 or more values in it, and that s also why I want to know how to return a struc in a class.

When I look your code, when I see this :
MyClass::GetMyStruct(MyStruct *pS)
I think pS (witch is a pointer of MyStruct) is a parameter of the function GetMyStruct, but this is not what I want. I want the function return a value typed MyStruct.

So, now, I modify the light code to use pointer instead, but it doesn't work anymore, and I have the same error.

(No change in this code Arduino)
Code: (Arduino) [Select]

#include <SoftwareSerial.h>
#include <MyClass.h>

MyClass C(10, 11); //rx et tx

struct MyStruct {
  bool MyBool;
  String MyString;
};

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:
  MyStruct S = C.GetMyStruct(); //The error is in this line

}



(Just add the * in the line MyStruct *GetMyStruct();)
Code: (header) [Select]

#pragma once
#include <Arduino.h>
#include <SoftwareSerial.h>
class MyClass
{
public:
MyClass(int rx, int tx);
~MyClass();

struct MyStruct {
bool MyBool;
String MyString;
};

MyStruct *GetMyStruct();

private:
SoftwareSerial *MySerial;

};


(code adapted to use the pointer)
Code: (cpp) [Select]

#include "MyClass.h"

MyClass::MyClass(int rx, int tx)
{
MySerial = new SoftwareSerial(rx, tx);
}

MyClass::~MyClass()
{
}
MyStruct MyClass::GetMyStruct()
{
MyStruct *S(0);
S = new MyStruct;
S->MyBool  = false;
S->MyString = "";
//Code
//...
return S;
}


I tryed too with :
   return *S;

But the error is still (In function 'void loop()': ) :
Quote
error: conversion from 'MyClass::MyStruct' to non-scalar type 'MyStruct' requested
Thanks for your help,
St├ęphane

Peter_n

You declare a type of MyStruct in a normal way and also inside the class, that is two times with the same name.
In the function you create a pointer, but where is the data located ?
According to the funciton, the return type is a MyStruct (the one that was declared inside the class), but you return a pointer.

I didn't notice it before, but inside the MyString is the object String. The actual characters of that string are located somewhere else, it can cause memory trouble.
Some of us don't use the String object on a Arduino Uno or Leonardo, because it is hard to predict what it will do with memory.

Do you want the data (string and error) inside the class ? or only return it ? If it needs to be returned, it can be a class or a pointer to a struct via a parameter. But you can't return a normal struct.

Why not use an empty string to indicate it is empty and not valid ?


When a timestamp is needed, an extra variable is needed.
Code: [Select]

struct myStruct
{
  int error;
  unsigned long timestamp;
  char buf[40];
};

int MyClass::GetMyStruct( myStruct *pS)
{
  pS->error = 0;
  ..
}

I would not add this myStruct to the myClass, not by definition, not declared as variable and not as pointer.
As you can see, I used a pointer to myStruct as parameter again. That is because the myStruct is now a normal struct, and not an object.


My advice: Have a good night sleep  :smiley-sleep: and start all over again. Don't make something that you don't understand. Arduino can do old style 'c' as well as 'c++'. You can create an array of struct and once that is working convert it into an object.

Stephane_63

Thanks Peter.
Ok, I will do another class (instead of the struct) to be returned by the function.

christop

You sure can return a struct or class from a function, such as:
Code: [Select]

MyStruct MyClass::GetMyStruct()
{
MyStruct S;
S.MyBool  = false;
S.MyString = "";
//Code
//...
return S;
}

There's no problem with that.

But make sure you're not defining a MyStruct struct both inside and outside of your class. Those are two different struct types, even though they have similar names (one is called MyStruct and the other is called MyClass::MyStruct).

Nick Gammon

This compiles, but I think it has problems:

Main sketch:

Code: [Select]
#include <SoftwareSerial.h>
#include "MyClass.h"
MyClass C (10, 11); //rx et tx
void setup()
{
}
void loop()
{
  MyStruct * S = C.GetMyStruct();
}


MyClass.cpp:

Code: [Select]
#include "MyClass.h"
// constructor
MyClass::MyClass(int rx, int tx)
{
 MySerial = new SoftwareSerial(rx, tx);
}
// destructor
MyClass::~MyClass()
{
}
MyStruct * MyClass::GetMyStruct()
{
 MyStruct *S(0);
 S = new MyStruct;
 S->MyBool  = false;
 S->MyString = "";
 //Code
 //...
 return S;
}


MyClass.h:


Code: [Select]
#pragma once
#include <Arduino.h>
#include <SoftwareSerial.h>

typedef struct MyStruct
  {
  bool MyBool;
  String MyString;
  };

class MyClass
{
public:
  MyClass(int rx, int tx);
  ~MyClass();
 
  MyStruct * GetMyStruct();

private:

  MyStruct myStruct_;
  SoftwareSerial *MySerial;

};


The problem is every time you call GetMyStruct you will do another "new" so you will run out of memory in a millisecond or so. You need to redesign.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

michinyon

Quote
public:
   MyClass(int rx, int tx);
   ~MyClass();

   struct MyStruct {
      bool MyBool;
      String MyString;
   };

   MyStruct GetMyStruct();
This code does not create an instance of MyStruct as a data member of the class.

michinyon

Issues you should think about,  as I see them:

You have two different definitions of MyStruct,  which then causes confusion about which one you are using,   and requires the class identifier to disambiguate,  even though you declared it public.   Even if "legal",  it indicates a poorly-thought-through strategy.

a struct really ought to be a fixed size entity.   Your's isn't.  It has a String in it.   Even if that is legal now, it is a poor idea.

You haven't thought through the issues with memory allocation,  including both explicitly defined dynamic memory allocation and the implicit allocation through the use of String.   Which piece of the code "owns" the allocated memory,  and is responsible for deallocating it later ?   What is the scope and persistence of each piece of memory ?   What is the hazard of trying to later access memory which has been de-allocated and reused for something else ?  You didn't think this through ?  Let me guess,   you are a Java programmer.

Why do you even need this struct ?     Just make myBool and myString data members of the class.   Your code I mentioned in reply #8  doesn't even actually create a struct.


michinyon

If you have a program where some code or class A  is wanting to get some information from a code or class B,    my recommended approach would be to have A ( and not B ) own the memory that the information is passed in.

What this means,  is that A could have a piece of memory that is static and persistent,  or functional scope,  or dynamically allocated.   It passes a point to that memory to B,   and B fills in the information required into A's memory ( which could be a struct ).    B therefore doesn't need to know about the lifetime of the memory,  and the programmer of B doesn't need to care about it.  It's up to A,  whether to keep the information permanently,  or use it once and chuck it away.


Peter_n

Stephane_63, michinyon points out what I wrote about. The Arduino Leonardo is a microcontroller. To have a practical and reliable project, you need to avoid memory trouble. Some of us don't even use the String object for these projects.
If you want to learn about C++ from an academical point of view, you can create classes, and return classes, and have a String object in a class. However, at this moment, I don't see how your code will evolve in good code, because of the problems that michinyon mentions.

I think you have two choices:
1 ) Use the Arduino Leonard, and limit yourself by using plain old good code, in which it is clear what the consequences are for every source code line.
2 ) Go wild with objects, learn them inside out, and use a ARM processor. Like the Arduino Due, or the Arduino Zero, or the Teensy 3.0 or 3.1 or the new Teensy LC (Low-budget and Crispy  :D ).

Go Up