Let's say I have a String class that can be constructed with a char array pointer. Is there any crazy way, through some magical operator overload, free function, or preprocessor macro to make Python-like syntax work, autocasting a char array literal to a String? To make this compile:
String a = "Foo".substr(1);
I suppose a wild pre-compile sed statement would do it, but something within the abilities of clang would be preferred.
For C++11 and beyond
#include <iostream>
#include <string>
int main() {
using namespace std::string_literals;
auto a = "foo"s.substr(1);
}
If you wanted to write this for your own String class then the way to get the same behavior would be to roll your own user defined string literal, and then do the same
#include <cstddef>
class Str {
public:
explicit Str(const char*) {}
Str substr(int) { return *this; }
};
Str operator"" _s (const char* input, std::size_t) {
return Str{input};
}
int main() {
auto s = "something"_s.substr(1);
}
Related
I want to be able to pass a string literal to a class instance and also check at compile time for certain conditions on the string literal. But I want the string checking to be done by the class somehow. I have a sample code with roughly what I'm trying to achieve:
#include <type_traits>
#include <string>
#include <string_view>
class TestString
{
public:
constexpr bool TestStringCondition(const char* name) noexcept
{
return std::string_view(name).find('a') != std::string_view::npos; //random condition
}
constexpr TestString(const char* name) noexcept:
m_name(name)
{
static_assert(TestStringCondition(name), "error message");
}
const char* m_name = nullptr;
};
int main()
{
static constexpr const char* const name = {"foo"};
static const TestString testString { name };
}
I tried various options (templates, char_traits, etc.) but keep getting compiler error "static_assert expression is not an integral constant expression". It seems to not be happy with the stringliteral passed as a parameter as I can do the assert check outside the class. I cannot use any c++20 features yet and I want a way avoiding Boost. Does anyone know a way?
The following works but I am unsure if it is what you want:
#include <type_traits>
#include <string_view>
template<const char *const t_name>
class TestString {
public:
static constexpr bool TestStringCondition(const char *name) noexcept {
return std::string_view(name).find('a') != std::string_view::npos; // random condition
}
constexpr TestString() noexcept {
static_assert(TestStringCondition(t_name));
}
};
constexpr char okay[] = "okay"; // array, so it is a static object with linkage
int main() {
static const TestString<okay> testString{};
}
My code in VS2019 is not working.
In the last line of code compiler throws an error.
#include <iostream>
#include <string>
using namespace std;
struct struct1
{
string name;
};
void main()
{
struct1* obj1 = new struct1();
obj1->name = "Hello";
// compiler says 'initializing': cannot convert from 'const _Elem *' to 'char [25]'
char str[25] = (obj1->name).c_str();
}
c_str() returns a pointer to the start of the string's character data. You'll need to copy the characters into your array using something like strncpy().
there is a lot of ways to do this
but first you should change the manner of char array initialization because you made it wrongly.
this is the same program but working
#include <iostream>
using namespace std;
struct struct1
{
string name;
};
int main()
{
struct1* obj1 = new struct1();
obj1->name = "Hello";
char str[25] = "";
memcpy(&str,obj1->name.c_str(),obj1->name.size());
cout << str << endl;
}
This question already has answers here:
C++ Adding String Literal to Char Literal
(8 answers)
Closed 3 years ago.
I am new to c++ templates. I am having trouble converting a templated member object to std::string. My code below fails to print the character 'c' (on my machine it prints 'R' instead) and I can't find a way to make it work. Is there a way to do this when using char as type T?
#include <iostream>
#include <string>
template<class T>
class A {
public:
T t;
A(T t1):t{t1}{};
std::string toString(){
return "" + static_cast<char>(t);
};
};
using namespace std;
int main()
{
A<char> a{'c'};
cout << a.toString() <<"\n";
return 0;
}
The problem with
return "" + static_cast<char>(t);
is that "" isn't a std::string object, it's a literal string which is a constant array of characters. As an array it will decay to a pointer to its first element, and you add static_cast<char>(t) to the pointer.
This new pointer will then be used as a null-terminated string to create a std::string object, and that usage of the invalid pointer leads to undefined behavior.
You can construct a std::string directly from a single character and return it as
return std::string(1, static_cast<char>(t));
Change
return "" + static_cast<char>(t);
to
return std::string() + static_cast<char>(t);
With the original, you have a string literal treated as a pointer.
If std::string is constructible from T you can use the following solution:
#include <iostream>
#include <string>
template<class T>
class A {
public:
T t;
A(T t1):t{t1}{};
std::string toString(){
return {t}; // <- this constructs a string from t
};
};
using namespace std;
int main()
{
A<char> a{'c'};
cout << a.toString() <<"\n";
return 0;
}
Live code here.
However this will fail if std::string is not constructible from T. In that case you can use template specialisation to handle the conversion properly.
I'm trying to implement a class (C++) with an enum (with the permitted parameters). I got a working solution, but if I try to extend the functionality I get stuck.
Header data_location.hpp
class DataLocation
{
private:
public:
enum Params { model, period };
std::string getParamString(Params p);
};
Program data_location.cpp
string DataLocation::getParamString(Params p){
static const char * ParamsStrings[] = {"MODEL", "PERIOD"};
return ParamsStrings[p];
}
The array ParamsStrings should be generally available in the class, because I need a second method (with inverse function) returning the enum value given a string.
If I try to define the array in the header I get the error:
in-class initialization of static data member ‘const char* DataLocation::ParamsStrings []’ of incomplete type
Why is the type incomplete? The compiler is for sure able to counts the strings in the array, isn't it?
In case there is no way to get my code working, is there an other way? With 1) no XML; 2) no double definition of the strings; 3) not outside the class; 4) no in code programmed mapping.
In class (header) use keyword static and initialize it outside (.cpp) without the static keyword:
class DataLocation {
public:
enum Params { model, period };
string getParamString(Params p);
static const char* ParamsStrings[];
// ^^^^^^
};
const char* DataLocation::ParamsStrings[] = {"MODEL", "BLLBLA"};
//^^^^^^^^^^^^^^^^^^^^^^^^
The code you have posted is perfectly fine.
Here's the proof:
#include <iostream>
#include <string>
struct DataLocation
{
enum Params { model, period };
std::string getParamString(Params p){
static const char * ParamsStrings[] = {"MODEL", "PERIOD"};
return ParamsStrings[p];
}
};
int main()
{
auto a = DataLocation();
std::cout << a.getParamString(DataLocation::model) << std::endl;
return 0;
}
The error message you are getting is not to do with definition of a static data member in an inline function - that's allowed.
There's something else you're not showing us.
The main issue in my question (the second part) was that if I split the class in .hpp and .cpp the definition of the array (I mixed *char and string) has also to be split:
// data_location.hpp
class DataLocation {
static const char * ParamsStrings[];
}
// data_location.cpp
const char * ParamsStrings[] = {"MODEL", "PERIOD"};
At the end I introduced a consistency check to be sure that the number of values in enum growths as the number of strings. Because the array in C++ is somehow limited I had to go for a std::vector (to get the size).
Code for data_location.hpp
#ifndef DATA_LOCATION_HPP_
#define DATA_LOCATION_HPP_
#include <string>
#include "utils/dictionary.hpp"
extern const char* ENV_DATA_ROOT;
struct EDataLocationInconsistency : std::runtime_error
{
using std::runtime_error::runtime_error;
};
struct EDataLocationNotValidParam : std::runtime_error
{
using std::runtime_error::runtime_error;
};
class DataLocation
{
private:
std::string mRootLocation;
static const std::vector<std::string> msParamsStrings;
static bool msConsistenceCheckDone;
public:
DataLocation();
std::string getRootLocation();
std::string getLocation(Dictionary params);
enum Params { model, period, LAST_PARAM};
std::string Param2String(Params p);
Params String2Param(std::string p);
};
#endif
Code for data_location.cpp
#include "data_location.hpp"
#include <string>
#include <cstdlib>
using namespace std;
const char* ENV_DATA_ROOT = "DATA_ROOT";
bool DataLocation::msConsistenceCheckDone = false;
DataLocation::DataLocation() {
mRootLocation = std::getenv(ENV_DATA_ROOT);
if (not msConsistenceCheckDone) {
msConsistenceCheckDone = true;
if (LAST_PARAM+1 != msParamsStrings.size()) {
throw(EDataLocationInconsistency("DataLocation: Check Params and msParamsStrings"));
}
}
}
string DataLocation::getRootLocation() {
return mRootLocation;
}
string DataLocation::getLocation(Dictionary params) {
// to do
return "";
}
const vector<string> DataLocation::msParamsStrings = { "MODEL", "PERIOD", ""};
string DataLocation::Param2String(Params p) {
if (p>=msParamsStrings.size()) {
throw(EDataLocationNotValidParam("Parameter not found"));
}
return msParamsStrings[p];
}
DataLocation::Params DataLocation::String2Param(string p) {
for (int i = 0; i < msParamsStrings.size(); i++) {
if (p == msParamsStrings[i])
return (Params)i;
}
throw(EDataLocationNotValidParam("Parameter not found"));
}
And also a unit test:
#include <boost/test/unit_test.hpp>
#include "data_location.hpp"
#include <string>
using namespace std;
BOOST_AUTO_TEST_SUITE( data_location )
BOOST_AUTO_TEST_CASE(data_location_1) {
DataLocation dl;
auto s = dl.getRootLocation();
BOOST_CHECK_EQUAL(s, "/home/tc/data/forex" );
BOOST_CHECK_EQUAL(dl.Param2String(DataLocation::period),"PERIOD");
BOOST_CHECK_EQUAL(dl.String2Param("PERIOD"),DataLocation::period);
BOOST_CHECK_THROW(dl.String2Param("SOMETHING"), EDataLocationNotValidParam);
BOOST_CHECK_THROW(dl.Param2String((DataLocation::Params)100), EDataLocationNotValidParam);
}
BOOST_AUTO_TEST_SUITE_END()
C++ is very picky about what it will let you initialize inside of a class definition; there are some particularly non-intuitive rules surrounding static members. It all has to do with the ODR, and why all the rules are the way they are is not especially important.
To cut to the chase, making your array a static constexpr const member should shut the compiler up. With the C++11 standard, the restrictions were relaxed a bit, and one of the new stipulations was that static constexpr members can be initialized inline. This is perfect for your application, since the strings in your array are compile-time constants.
The recent g++ compiler which support C++0x or later compiles thus code. Pure C compile compiles, too. Because strings in initialization like {"MODEL", "PERIOD"}; implemented as const char * pointer to the char array.
I tried to initialize an array of string in class as following:
class Haab{
string str[];
Haab(){
str[] = {"abc", "abd", "abe"};
}
};
But the Devc++ 5.6.1 reports a warning:
[Warning] extended initializer lists only available with -std=c++11 or -std=gnu++11 [enabled by default]
Is this way of initializing arrays in class illegal? If so, how to properly initialize the array? Thank you.
The given code,
class Haab{
string str[];
Haab(){
str[] = {"abc", "abd", "abe"};
}
};
is invalid in a number of ways:
string str[]; declares a member array of unknown size. Can't do that.
In str[] = {"abc", "abd", "abe"};, the expression str[] uses the [] indexing operator without specifying the index.
If that parsed, then the = would denote assignment of a single string.
Here's one C++03 way to do things:
#include <string>
#include <vector>
namespace qwe {
using std::string;
using std::vector;
class Haab
{
private:
vector<string> str_;
public:
Haab()
{
static char const* const data[] = {"abc", "abd", "abe"};
for( int i = 0; i < 3; ++i )
{
str_.push_back( data[i] );
}
}
};
} // namespace qwe
There are also other C++03 ways, all of them ugly (as the above).
In C++11 and later it can be done with more elegant & simple notation;
#include <string>
#include <vector>
namespace qwe {
using std::string;
using std::vector;
class Haab
{
private:
vector<string> str_;
public:
Haab()
: str_{ "abc", "abd", "abe"}
{}
};
} // namespace qwe
but this does still not compile with Visual C++ 13.0 (it does compile with MinGW g++ 4.9.1).