I have following enum in a class.
enum Attributes: String, CustomStringConvertible {
case eventDate
case eventName
case eventType
case country
var description: String {
return self.rawValue
}
}
When I try have the following code, compiler complains with following error.
var attributesList: [String] {
return [
Attributes.eventDate, //<-- Compiler error on this row
Attributes.eventName,
Attributes.eventType,
Attributes.country]
}
Cannot convert value of the type 'Attributes' to expected element type 'String'
Shouldn't the "CustomStringConvertible" protocol return the "description"?
What is wrong in the above code?
TL;DR - It doesn't work because an array of Attributes cannot be assigned to an array of Strings, they are both mismatched types, and Swift does not do automatic conversion between types, and an explict conversion needs to be specified.
In Swift, when you initialise an array using an array literal, the following happens under the hood:
let words = ["hello", "world"]
The compiler recognises that an array literal is being assigned to a variable named words. Since we have not specified the type of the words, implicitly an array is assumed. The type of the elements underlying the array is determined based on the contents of the array literal.
In this case, the array literal is a collection of String types; this is easily understood by the compiler
Since the LHS type is an array, the RHS structure is an array literal, and since the LHS type (Array) conforms to a pre-defined protocol called ExpressibleByArrayLiteral which has an associated type constraint to match Element, the compiler will actually be converting our line to the following
Example:
let words = [String].init(arrayLiteral: ["hello", "world"]) // we do not call this init directly
This is how initialising with array literals work. In the above example, since we did not specify the type of array, the implicit type setting will work. If we specified a mismatched type, the assignment will fail, since ExpressibleByArrayLiteral requires the associated Element type of the array literal and the actual array you are assigning to to match.
So the following fails:
let words:[String] = [1, 2] // array literal has Element=Int, array has Element=String
This also shows that there is no implicit type conversion between Int and String, even though Int conform to CustomStringConvertible.
In your case, you are trying to assign an array literal consisting of Attributes to an array of String. This is a type mismatch. This is the reason it fails.
If you state protocol conformance, the following line will work:
var attributesList: [CustomStringConvertible] {
return [
Attributes.eventDate,
Attributes.eventName,
Attributes.eventType,
Attributes.country
]
}
// note that we have an array of CustomStringConvertible protocol,
// each element here is still of Attributes type
// a type conforming to a protocol can be cast to an instance
// of that protocol automatically
// The above initialisation works simply because the following
// also works without any further action required
// let a:CustomStringConvertible = Attributes.country
If you really want a list of string values, you need to map this to a string explicitly:
var attributesList1: [String] {
return [
Attributes.eventDate,
Attributes.eventName,
Attributes.eventType,
Attributes.country
].map { $0.description }
}
var attributesList2: [String] {
return [
Attributes.eventDate.description,
Attributes.eventName.description,
Attributes.eventType.description,
Attributes.country.description
]
}
Related
I am trying to return a string from a array with 4 hexadecimal values. I have a huge list of hex arrays and need to use it in multiple functions. That's why I want to create a function of comparing the hex arrays and return a string.
int comparehex(byte hex[]){
char * buffer = new char [16];
byte hex1[4] = {0x4C, 0x79, 0x52, 0xA8};
byte hex2[4] = {0xC5, 0x86, 0xA4, 0xB5};
for (byte i = 0; i <=3; i++){
if (hex1[i] != hex[i]){
break;
}
if (i == 3){
return "string";
}
}
return false;
}
The code that I wrote won't even compile:
main.cpp: In function ‘int comparehex()’:
main.cpp:46:14: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
46 | return "string";
| ^~~~~~~~
| |
| const char*
How can I return a string?
Function Declaration general rule:
return_type function_name( parameter list );
in your case:
int comparehex(byte hex[])
return_type : int
function_name : comparehex.
parameters : byte array.
As per declaration, function is supposed to return int.
but as per your code
return "string"; // returns a STRING
}
}
return false; // return a boolean
To return a string output, declare the return_type as std::string as in.
to do that you need to include "string" library.
#include <string>
std::string comparehex(byte hex[])
Although the return of boolean is cast to int (implicit type conversion).it is not a good practice and is considered unsafe.
refer to Implicit type conversion in C for more details.
Here is an example of returning const char* with nullptr as a special type of "sentinel value" to mean "false":
There are a lot of potential things to address here, and we don't really understand your purpose of the function or use-case, but let me just address your immediate code and one viable solution.
There are many potential ways to handle this. Again, here is just one. But, I have to make some assumptions to even answer. Let's assume that:
Your function needs to return either a string literal OR false (or something equivalent to represent this).
This means you will NOT be generating a string at run-time inside the function. You will ONLY return a string literal (meaning: a compile-time-constant string with double quotes around it like this: "some string literal") which is set in your source code and fixed at compile-time. Otherwise, my example may not meet your needs, and would need some changes.
You will never need to return true. Rather, the existence of a string indicates true as well.
In this case:
// 1. Change this:
int comparehex(byte hex[])
// to this:
const char* comparehex(byte hex[])
// 2. change this:
return false;
// to this:
return nullptr;
// now this line is fine too:
return "string";
Now, the function returns either nullptr to indicate false, OR a string literal such as "string".
You'd simply check to see if "false" was intended by checking like this:
const char* str = comparehex(some_hex_array);
if (str == nullptr)
{
// `comparehex()` essentially returned `false`, so do what you need to do here
}
else
{
// str was set to some string, so use its value (ex: print it)
printf("%s\n", str);
}
Final notes:
Again, if you're generating a new string inside the function at run-time, rather than returning a string literal set at compile-time, the above 2 changes are not sufficient.
Also note that the above code is rather "C-like". It is perfectly valid C++, but only one of many ways to handle the above scenario.
And lastly, nullptr here can be considered a type of "sentinel value", which means simply that it is a special value of your return type (const char*) to indicate a special meaning: false in this case. And therefore, by extension, this sentinel value of nullptr also possesses the special meaning of whatever you intend "false" to mean.
Related
For another generic example of returning const char*, see my const char * reset_cause_get_name(reset_cause_t reset_cause) function in my answer in C here: STM32 how to get last reset status. I don't return NULL (the C analogue to C++'s nullptr) for cases where no match is found, but I could. (In my example I set it to "TBD" instead of NULL).
See also: What exactly is nullptr?
In your case, you could add another parameter for passing by reference:
int comparehex(byte hex[], std::string& result_string);
In your code, before returning, set the result_string parameter to the string you want to return.
I'm trying to convert two types of values: char and uint8.
I have a function someFunction(TLabel *label, TEdit *edit). Inside of this function, I have a variable char var[6+1].
What I'm trying to do:
Get an Alphanumeric text (input of TEdit *edit), convert this text and put it inside of the var.
I know that if I call this function and put the *label and the *edit I can get whatever I want, but the problem is the conversion to associate the text of *edit in the var.
An example of my code (inside of the function):
char var[6+1];
label->Text = "Some text";
var = edit->Text;
//I will put var value inside of an another char (like a #define) that is in a struct, but doesn't matter for now
my_struct.valueOfVar = var;
And I have another function, it's the same code but the valueOfVar above is a uint8, and I can't convert it, too:
uint8 valueOfAnotherVar[6+1];
The issue is not with the types of the elements in the arrays, but rather that you simply can't assign one static array to another like this:
char a[7];
uint8 b[7];
a = b; // error
Instead, if you want to copy the values in the arrays, you can use std::copy like this:
std::copy(std::begin(b), std::end(b), std::begin(a));
If you have a dynamic array, you can still use std::copy, but you have to use the size of the array:
std::copy(b, b + N, a);
Make sure that both a and b point to at least N elements, otherwise you'll invoke undefined behavior.
The Text of a TLabel or TEdit is a System::String, which is an alias for System::AnsiString in C++Builder 2007 and earlier, and for System::UnicodeString in C++Builder 2009 and later. You did not say which version you are using, but it makes a big difference in your example.
You can't assign anything directly to a fixed array, like char[7] or uint8[7], by using operator= like you are attempting to do. You will need to instead convert and copy the String data into the allocated memory of the arrays, eg:
char var[6+1] = {};
label->Text = "Some text"; // OK - String has a constructor for 'const char*'
// if using CB2007 or earlier:
strncpy(var, edit->Text.c_str(), 6);
// if using CB2009 or later:
strncpy(var, AnsiString(edit->Text).c_str(), 6);
And then you can use one of these to copy the contents of var into valueOfVar:
memcpy(my_struct.valueOfVar, var, 7);
std::copy(var, var+7, my_struct.valueOfVar);
std::copy_n(var, 7, my_struct.valueOfVar);
I want to deserialize a variable and for that I have stored its type in a string just like this:
int var = 60;
auto type = typeid(int).toString;
writeln(type); //Prints "int"
But now, when I have to deserialize it, I can not just cast the string to a real type as it is required to do it at compile time. I could make a table and store alias to types or build a big switch block for every type, but it will eventually get really annoying. Does anyone have any suggestion?
I want to convert a bitfield to a string.
Visual Studio 2008 gives an invalid null pointer exception.
Maybe it has something to do with the size of the array. it must be 8 but the output says it is 4, but why?
class Converter
{
public:
string bitfieldToString (bool b_input[])
{
string c_conv;
int i;
for(i = 0; i < sizeof(b_input) ; i++)
{
if(b_input[i]=false){
c_conv.append("0");
}
else if (b_input[i]=true){
c_conv.append("1");
}
else c_conv = "Input is not a bitfield";break;
}
cout<<c_conv<<" "<< sizeof(b_input)<<endl;
return (0);
}
};
int main(void)
{
Converter converter;
bool b2[8] = {0,1,0,0,1,0,1,1};
converter.bitfieldToString(b2);
return (0);
}
Thank you!
Now everything works as intended.
and sorry for that dump question. I am new to C++.
The exception is because you return (0);. That's interpreted as a null pointer, used to initialise a std::string with a constructor which requires a valid pointer to a C-style string - not a null pointer.
That should be return c_conv;
The size mismatch is because b_input isn't an array. As a function parameter, bool b_input[] is a pointer. You can't pass an array to a function by value; and there's no way to determine the array size from the pointer alone. So sizeof(b_input) gives you the size of a pointer, not the array, and everything goes wrong.
There are a few options. You could pass the size as a second parameter; but that's error-prone. You could infer the size as a template argument, by taking the array by reference:
template <size_t size>
string bitfieldToString (bool (&b_input)[size])
You could use std::array or std::vector, which have handy size() member functions. (But be careful with vector<bool>, since it's a special case that doesn't always behave quite like a standard container.) Or you could use std::bitset, which has a handy to_string function that does exactly what you want.
Finally, enable your compiler's warnings - it should tell you not to use = where you mean ==. And there's not much point checking for the case of a boolean being neither true nor false. You can reduce the whole loop body to
c_conv.append(b_input[i] ? '1' : '0');
There is a lot wrong in your code.
First of all, the null pointer exception comes from return (0); at the end of the bitfieldToString fuction. You have defined it to return a string; when you return 0 instead, C++ thinks 0 is a char* pointer and will try to convert it - a NULLpointer - into a string, which will crash. You should probably be returning c_conv instead.
Second, sizeof(b_input) will always be the size of a bool pointer. On a 32-bit system it will be 4, on a 64-bit system 8. You cannot get the length of an array passed as argument with sizeof; you will need to add a length parameter to your function.
Third, inside your for loop, you are assigning to b_input[i] instead of comparing the values. Use ==, not =.
Fourth, in the last else branch, you are missing braces. Essentially, the break will always break out of the loop after the very first iteration.
For iterate with foreach loop and get dates over the files I need to have as input char [], but not string.
I wrote next code:
auto files = dirEntries ("E:\\ddd", SpanMode.shallow);
foreach (file; files)
{
char [] s = to!char[](file); //it's crap... and do not work...
writeln(file);
}
I use next method http://dlang.org/phobos/std_file.html#.getTimes
it's need as input char []: (in char[] name)
To get a mutable copy of immutable array you may use .dup:
char[] s = file.dup;
However in your case you don't need to do any conversion. getTimes accepts in char[] which is shortcut for scope const char[]. It it perfectly legal to pass immutable arrays as const arguments so it should "just work".
Your conversion line should look like:
... = to!(char[])(file.name);
This is because the shorthand template instantiation isn't valid for multiple tokens.
Second, there should be no need to convert to char[], as you said getTimes takes an "in char[]" which means it will not modify the string which means the immutable(char)[] can be passed without conversion.
You can also use :
const char[] s = cast(char[]) file;