{imgicourl}{zamok}
Другие записи за это число:
2019/12/29 - Рекламная лирика: Окна мигом
<< предыдущая заметкаследующая заметка >>
29 декабря 2019
гребаный C++

Слушайте, ну это мистика. В C++ Ардуино есть такой объект String, который в работе не в пример удобнее, чем char. Если нужно String превратить в char, то есть специальная функция .c_str() То есть, вот так работает:

String id = "ESP8266-test";
String login = "lleo";
String password = "h2dtGdGH";
client.connect( id.c_str() , login.c_str() , password.c_str() );

Везде и всегда, где нужно char, это работало. Но лишь в одной из функций одной библиотеки PubSubClient эта херня не работает! А у меня все параметры хранятся именно в String и брать их нужно оттуда.

В примерах у них конечно всё работает:

client.setServer("tailor.cloudmqtt.com",12399);

А вот так — нет:

String URL = "tailor.cloudmqtt.com";
client.setServer(URL.c_str(),12399);

Почему — загадка. В PubSubClient.h обозначено так:

boolean connect(const char* id, const char* user, const char* pass);
PubSubClient& setServer(const char * domain, uint16_t port);

Вышел из положения, временно нагуглив какую-то дикую конструкцию:

String URL = "tailor.cloudmqtt.com";
unsigned char* buf = new unsigned char[256];
URL.getBytes(buf, 256, 0);
const char *str = (const char*)buf;
client.setServer(str,12399);

Вот только это и сработало. Как думаете, что это, и почему во всех остальных функциях даже той же библиотеки все было норм?

UPD: Мне написали, что, вероятно, моя переменная исчезает раньше, чем функция ее использует... Это не совсем так. Волею судеб переменные этого рода у меня глобальные - они хранятся в объявленной глобально структуре как String имя и String значение. Специальная моя функция Srting CF(String имя) ищет "переменную" с этим именем и возвращает ее значение. То есть, в реальности это не URL.c_str(), а результат функции CF("url").c_str() Вот не пойму: с одной стороны все это реально хранится в глобальном пространстве и деться никуда не может. С другой стороны - это лишь некий результат, который вернула некая функция. И компилятор может его не хранить.

<< предыдущая заметка следующая заметка >>
пожаловаться на эту публикацию администрации портала
архив понравившихся мне ссылок
Оставить комментарий
Linux Safari Chrome
 Великобритания
4
0
AlexSa
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Весьма вероятно что объект URL удаляется раньше чем значение, полученное из его c_str используется где-то ниже по коду объектом client. При удалении URL строка может быть перезаписана любым мусором. Чтобы работало по-старому надо чтобы URL жил дольше чем server. В супер-простом случае - был бы глобальным.
Linux Ubuntu Firefox
 Москва
0
0
LLeo
Хм, объект URL вроде как бы хранится на моей стороне и не удаляется. На самом деле у меня некая база String с именами, ит объект выглядит как вызов CF("URL") - эта функция находит в базе нужное по имени. Из базы оно деться никуда не может и никто его там вроде не перезаписывает... Хотя спасибо, буду думать.
Linux Safari Chrome
 Великобритания
3
0
AlexSa
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
URL при таком синаксисе создания создаётся на стэке и живёт до первой объемлющей закрывающей скобки. Типа так:
{
String URL = blah;
if (tralala)
{
//code
}
} //URL is destroyed here

Не важно скобка это от функции или цикла и т. П.

Другой синтаксис String URL = new String("blah") ; позволит URL жить пока не вызовешь
delete URL; при этом URL будет указателем на String, т. е. вызов уже будет URL->c_str()
Но если забудешь delete - будет утечка памяти. Этот синтаксис создаёт объект в куче, не в стэке.

И это все - про сам объект строки. С базой и пр. не связано никак
Windows Firefox
 Москва
1
0
dredkin
На самом деле ваш вариант по смыслу совпадает с "нагугленной дикой конструкцией", но смотрится, конечно, симпатичнее.
Linux Safari Chrome
 Великобритания
0
0
AlexSa
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Примерно совпадает, да. Оба текут по памяти к тому же)
Windows Firefox
 Тольятти
0
0
oncle t
В с++ нет возможности задавать lifetime? От си, конечно, этого ждёшь, но не от плюсов.
Linux Safari Chrome
 Великобритания
1
0
AlexSa
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Не уверен что правильно понял что значит "задать lifetime". В C++ как и в C, нужно явно контролировать время жизни объекта (ну или куска памяти в случае C) , зато можно точно знать когда объект будет удалён, с гарантией вызова деструктора (кроме совсем аварийных случаев). В Java или C# это не так, мусор соберут когда (и если) соберут, это почти нельзя контролировать.
Далее встроенных механизмов контроля времени жизни 2:
1. Стэк, как в C. Закрылся блок - разрушен объект.
2. new и delete. Можно считать объектно-ориентированной оберткой над malloc и free.

Далее есть "магия" smart pointers. Внутри использует new и delete, но позволяет, например, не удалять объект пока есть хоть один _smart_ указатель на него (это shared_ptr). Сами умные указатели обычно являются небольшими объектами на стэке. Smart pointers нынче часть стандартной библиотеки, хоть и не являются конструкциями языка. Можно написать свой smart pointer, никто не запрещает. То есть это мегасупермощный и полезный, но, в некотором смысле, костыль.

Ссылки, кстати, на время жизни объекта влияния не оказывают, как и обычные указатели. Просто ссылки чуть "безопаснее".
Linux Safari Chrome
 Великобритания
1
0
AlexSa
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
И ещё - если это все в методе и параметр метода объявлен как String, то передаётся в метод временная копия объекта, которая удаляется при выхоже из метода, вместе с потрохами.
Так что, возможно, надо сделать
void f(const String& URL) {...}

а не

void f(String URL) {...}

Первая версия передаёт ссылку на оригинальную строку, вторая - копию.
Linux Ubuntu Firefox
 Санкт-Петербург
1
0
elapidae_2
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Поменяйте сигнатуру функции CF:
Была `String CF(const char *) {...}`
Сделайте `const String& CF(const char*) {...}`
Windows Safari Chrome
 Екатеринбург
4
0
Пёх
Поддерживаю. Метод setServer не делает свою копию строки, а сохраняет только указатель, переданный в аргументе.

Хоть бы упомянули об этом в заголовочнике в комментарии рядом.   Гнильё!
Windows Firefox
 Москва
0
0
dredkin
Очень на то похоже!
Ведь client.connect просто использует переданные ему данные и выходит, а setServer (даже по названию -"set") сохраняет это значение, а использует его кто-то другой позже.
Mac Safari
 Домодедово
6
0
id
Вы давно программировали комп с 1 килобайтом ОЗУ? Не гигабайтом и не мегабайтом - килобайтом? Вы правда считаете, что на такой машине хорошим тоном будет создавать копии таких объектов, как текстовые строки - которые, на минуточку, могут занимать десятки и сотни байт?

Автор этой библиотеки знает матчасть и готов к ограничениям, налагаемым ею. Леонид - нет. Не будьте, как Леонид. Не используйте в Ардуино String ни в чем сложнее хелловорлда.

P.S. Для константных строк есть хак: const char [] PROGMEM
Windows Safari Chrome
 Екатеринбург
0
0
Пёх
Ладно, держите решение с поддержкой экономии памяти.

Вместо одного метода два:
setServerAsNewCopy
setServerAsPointerToExisting

В заголовочнике обязательно комментарий ко второму методу: // Вызывающий код должен обеспечить, что строка в аргументе существует до конца жизни объекта PubSubClient.

Заодно такие наименования методов помогли бы дяде Лёне понять, что происходит.
Windows Firefox
 Калуга
0
0
DrGluck
Я вот всё время программирую под такие штуки. Вот недавно в 8К пытался поместить бутлоадер с USB стеком. Вместилось, но с трудом.
Windows Safari Chrome
 Домодедово
0
0
id
Что-то как-то дофига, нет? Ардуиновый USB бутлоадер под STM32 вроде заметно поменьше?
Linux Ubuntu Firefox
 HavinghastraatDA Alkmaar (The Netherlands)
0
0
Чук
4K. Включая криптографию. :-P
Windows Safari Chrome
 Германия
0
0
Alexander Kunis
Абсолютно верно. У меня однажды так было. Очень бесячая ошибка.
Лео, конечно, неверно привел код. У него URL обозначена как именованная переменная, а чтобы удалиться, она должена быть результатом функции. Собственно, Ллео в этом признался комментом ниже.
Mac Safari
 Германия
0
0
skepner
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
проблема во времени жизни, очевидно. во втором (работающем) случае место аллокируется и не освобождется, в первом варианте оно освобождается при выходе из блока
Linux Safari Chrome
 Испания
0
0
Мыш
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Так у тебя в функцию параметр передается по указателю
Указатель указывает на адрес в пямяти, в котором лежит то что тебе нужно. В данном случае - указатель на char
Если ты string переоборудываешь в char, у нее нет адреса, ибо ты это значение только что на коленке налепил
Чтобы адрес был, нужно отвести место в куче и положить туда свое значение, что собственно и делает оператор new и прочие ниже описанные заклинания
Linux Safari Chrome
 Великобритания
0
0
AlexSa
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Это не совсем верно.
1. Указатель может указывать и на стэк
2. Внутри себя string аллоцирует место на куче и c_str возвращает этот указатель.
Проблема, скорее всего, со временем жизни URL чей внутренний указатель используется после вызова деструктора ~string

UPD: c_str не обязан возвращать указатель на потроха объекта, но может. По-любому это что-то временное. http://www.cplusplus.com/reference/string/string/c_str/
Это если String - это на самом деле std::string
Windows Safari Chrome
 Москва
0
0
Max
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Если не ошибаюсь, String.c_str() выдает ссылку на внутренний блок данных где храниться строка. НО эти данные в любой момент могут поменяться при дальнейших изменениях строки. Возможно это вообще динамически выделяемый блок и на каждое изменение строки он будет в разных местах памяти.

Если функция требует константы то можно обмануть ее
client.setServer( (const char *)URL.c_str(), 12399 )

Но есть опасность, что функция не скопирует данную строчку себе и всегда будет ее искать по этому адресу.
Эта опасность будет и в вашей Дикой Конструкции: где димамически выделенная память под buf может быть "очищена" после выхода из функции. и там окажется что угодно.
Windows Safari Chrome
 Москва
0
0
Max
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Я с этим долго мучился когда через одну временную строку готовил json объект.
String=var1+var2+var3
json["123"]=String
String=var4+var5+var6
json["456"]=String

В итоге получал или мусор или последнее значение String во всех json
Linux Safari Chrome
0
0
Gnezdo
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Как конкретно оно не работает? Ошибка компиляции? Исполнения?
Linux Safari Chrome
 Москва
0
0
LLeo
Просто передает пустую строку.
Mac Safari Chrome
 Москва
0
0
Заливочкин Евгений
Ну, собственно, да - основная проблема именно в том, то объект класса PubSubClient сохраняет у себя указатель на нуль-терминированную строку, а метод объекта c_str() возвращает указатель на временную нуль-терминированную строку, и гарантирует её (строки) валидность только до удаления или модификации экземпляра класса String.
Собственно, это серьёзный недостаток класса реализации класса PubSubClient. Аналогичный эффект будет иметь место и без использования класса String. Например,
char buf[100];
strcpy(buf, "name");
strcat(buf, ".com");
client.setServer(buf, 443);
После выхода их процедуры/функции/метода содержимое переданного указателя будет не определено. А т.к. PubSubClient хранит именно указатель, то и обращение пойдёт неизвестно куда.

Методов решения проблемы много. Лучший вариант - это внести исправления в PubSubClient, в метод setServer, чтобы он создавал копию строки. А так — нужно просто позаботиться, чтобы указатель, передаваемый в PubSubClient, не «терялся». Предложенный метод с new плох тем, что не освобождает после себя память.

Леонид, если нужно, всегда готов помочь код подправить!
С уважением,
Заливочкин Евгений
Linux Safari Chrome
 Москва
0
0
LLeo
Честно сказать, я вообще хотел отойти от типа String.

Задача в общем виде такая: система, которую я пишу, это некий интерпретатор внутреннего языка текстовых скриптов. Поэтому все переменные, которыми он в итоге оперирует, это текстовые строки (даже если числа) со своим именем. То есть по значению X находится строка 100, а по значению URL строка pool.mqtt.ru и так далее.

Сейчас у меня при старте жестоко выделяется структура из 50 переменных String с именами String и считается, что для несложных скриптов этого числа переменных хватит, а тип String (как хочется надеяться) распределит память оптимально.

Но понятно, что любое жесткое выделение числа переменных - это уже неоптимально. Вдобавок мы видим, какие проблемы возникают типа этой.

Вероятно для этой задачи (хранение произвольного числа произвольно измеряемых переменных по произвольным именам) мне лучше использовать два буффера char, где а первом перебирать все имена, чередующиеся нулем, пока не найду нужное имя, а во втором - тем же способом найти нужное по счету значение. И все это выделять с помощью malloc. Но только я пока не решил, они ли более удобного способа. М что делать, если переменная меняет знание на более короткое или более длинное. Двигать оба массива?

Что посоветуете?
Firefox
 Австралия
0
0
bkon
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
std::map ?
Linux Safari Chrome
 Москва
0
0
LLeo
Как это практически написать?
Linux Safari Chrome
 Великобритания
0
0
AlexSa
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Вот как тут https://www.google.com/amp/s/www.geeksforgeeks.org/searching[...] только ключ вместо char будет String.

Либо, если хочется C-style
и malloc-а, но динамической длины строк, можно ж заиметь массив указателей. То есть char*[50] (он же char**) или, если он должен иметь динамический размер - std::vector<char*>. Далее элементы уже нужно руками аллоцировать через new или malloc, а потом когда-то удалять через delete/free, сам такой массив будет хранить только указатели. С точки зрения C++ кошмар, конечно, но позволит не вдаваться во множество тонкостей C++.

А нынешняя проблема похоже связана с тем, что из массива ты достаёшь копию строки, а надо ссылку. То есть где-то вместо String my = arr[x] ; нужно написать const String& my = arr[x] ;
Понять почему (с нуля) займёт достаточно много времени, это большой кусок знаний по плюсам, поэтому выше посоветовал C-style решение с char **.

А, ещё любое решение с malloc/new будет фрагментировать кучу, то есть если памяти совсем мало, через некоторое время новое значение может не влезть т. к. не будет непрерывного куска памяти нужной длины. Если самому всё двигать в большом массиве char, этой проблемы не будет, но придётся больше пить и курить.
Windows Safari Chrome
 Москва
0
0
wewewxd
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
только мапов нет у Ардуины. Точнее, есть, но в доморощенных библиотеках.
Windows Firefox
 Fremont
0
0
Евгений-опятьнелогинится
Возможно, лезу не в свое дело и все понял не верно, но если речь о командах, то есть теоретическая возможность хранить какой-нибудь хэш, который будет короче названия команды. Тогда при появлении строки нужно считать хэш и искать его. Тогда команды могут быть длиннее и в большем количестве. Какой-нибудь CRC16 может подойти, только проверить, чтоб коллизий не было.
Linux Safari Chrome
 Москва
0
0
LLeo
Была такая идея, но, честно говоря, не так они обычно длинные, имена, чтобы хэш что-то сэкономил. Вот три же URL - это имя занимает четыре байта с нулем вместе. Что мы сэкономим, устроив хэш?
Linux Safari Chrome
 Нидерланды
0
4
умяумяу
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Переписывайте все на каком-нибудь нормальном языке.
Windows Safari Chrome
 Москва
1
1
Ilya Glubokov
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Дело в кривоватой реализации метода setServer класса PubSubClient. Содержимое строки - аргумента domain не копируется, а просто значение указателя (адрес строки) запоминается в переменной класса. Такая реализация метода setServer требует, чтобы передаваемая ему в качестве аргумента строка domain жила всё время существования объекта класса PubSubClient и строго по данному адресу.

Вот как выглядит реализация (https://github.com/knolleary/pubsubclient/blob/master/src/Pu[...]):
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
this->domain = domain;
this->port = port;
return *this;
}
Просто запоминается адрес в переменной класса domain, которая объявлена в заголовочном файле (https://github.com/knolleary/pubsubclient/blob/master/src/Pu[...]) в строке 108 как:
const char* domain;
Linux Firefox
 Израиль
4
1
Mason
Вы правы в диагностике, но я не согласен с вами в оценке. Это не кривая реализация, а разумное ограничение. В системе, где каждый байт на счету, не всегда оправдано писать generic код. Подразумевается, что пользователь достаточно продвинут, чтобы посмотреть исходники и понимать, как работает память.

Другое дело, что в заголовочном файле я бы написал примечание, мол полученный параметр "domain" будет сохранен как есть и использован все время жизни объекта
Linux Firefox
 Франция
1
2
Максим Дементьев
метод перегружается три раза:
PubSubClient& setServer(IPAddress ip, uint16_t port);
PubSubClient& setServer(uint8_t * ip, uint16_t port);
PubSubClient& setServer(const char * domain, uint16_t port);
Не удивлюсь, что вызывается с uint8_t *, который не как строка интерпретируется.

А надо при вызове лишь добавить привидение, похоже:
client.setServer((const char *)URL.c_str(),12399);

Вообще, char - очень сложный предмет: он в плюсах бывает аж трёх типов. К этому наслаивается типы целочисленные той же длинны.
Linux Firefox
 Франция
0
0
Максим Дементьев
Надо пофиксить код на этой странице, я упомянул в ЖЖ:

https://lleo-kaganov.livejournal.com/399930.html?thread=3259[...]

Удалось ли попробовать моё решение?

Вообще, надо руки отрывать за такую перегрузку (char & int8)!!!
Mac Safari Chrome
 Нижний Новгород
2
0
tartaglia
> When you modify the String object, or when it is destroyed, any pointer previously returned by c_str() becomes invalid and should not be used any longer.

Зачем ругать C++ всуе? Этот язык предельно честен в том отношении, что ему на вас наплевать. C++ не обещает вытирать вам сопли, он перед вами никогда не виноват. Если вы чего-то не понимаете, это исключительно ваша проблема. Если вы встречаете человека, который заявляет, будто знает C++ больше чем наполовину, то вы встретили неадекватного человека. Спросите его, что такое "perfect forwarding", например. C++ — самый сложный язык, который мне попадался в жизни. Его можно любить, можно ненавидеть, при этом одинаково не понимая.

Как я понимаю, вы поставлены в условия, когда вам приходится пользоваться именно C++, хотя вы не открывали даже первоисточник вроде Страуструпа (с моей точки зрения — самоубийственное легкомыслие). Если без лирики, я бы на вашем месте сделал что-то вроде

#include <cstddef>
#include <cstring>

const char *str_stash(const char *src) {
  std::size_t n = std::strlen(src);
  return std::strcpy(new char[n + 1], src);
}

const char *str_stash(const String &str) {
  return str_stash(str.c_str());
}


(Не уверен насчёт файлов заголовков, но этих должно вроде хватить.) И пользовался вызовами типа client.setServer(str_stash(URL), 12399);

Разумеется, при этом всякий раз будет утекать память на буфер для сохранения копии строки, если это вас в принципе волнует. Разумеется, я в глаза не видел вашего Ардуино и его типа "String". Уже одно то, что они там используют в идентификаторах кэмелкейс, мне о многом говорит. С дества от этого кэмелкейса в глазах рябит, ненавижу.

P.S. Есть ли тут возможность сохранять форматированный текст с отступами, не затрудняя себя всякими nbsp?
Windows Firefox
 Ивано-Франковск
1
3
VP
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Нехуй юзать быдло-ардуино. Из ихнего быдло-ИДЕ, у которого из настроек только кнопка "СДЕЛАТЬ ПИЗДАТО", довольно затруднительно посмотреть листинг на асме после компиляции, из которого было бы ясно, где именно просираются полимеры, например.
Повторять до полного просветления: Ардуино ТОЛЬКО для прототипирования! Лично я зарезал штуки 3 буратинов на техническом интервью за признание, что они юзали сабж для продакшена.
Так что да, злорадствую: еще не на те грабли наступишь с этим замечательным поделием.
Mac Safari
 Домодедово
4
0
id
Режьте меня: я юзаю сабж для продакшена.

Потому что а) gcc «для ардуино» и gcc «для всех остальных» - это ровно один и тот же gcc, другого не завезли, и если он вдруг вам не нравится, у людей принято не хейтить а коммитить, и б) библиотеки для ардуино - реально лучшие по качеству кода в мире avr, и всякий кто пишет под avr «нативно» - либо уныло изобретает хромой велосипед, либо втайне юзает ровно те же ардуиновые библиотеки.

И в данном конкретном случае автор библиотеки прав, а хейтеры - нет. Потому что хейтерам до знания матчасти - как до Луны пешком, и что такое 1 килобайт ОЗУ они в принципе представить себе не могут.

А что до IDE, то тут все просто: либо человек может писать код в любом текстовом редакторе (а в Ардуино редактор не худший), либо он просто лишний в этой профессии. А эстеты вместо рыданий могут юзать VS Code, если «девы и игрища» в IDE им так необходимы.
Linux Safari Chrome
 Якутск
0
0
Соттник
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Ебака грозный
Linux Safari Chrome
 Москва
0
0
xor4xor
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
ну ты и лох.
на асме только нубы пишут, надо сразу в машинных кодах.
Linux Safari Chrome
 Москва
0
0
LLeo
Я почти десять лет писал на самых разных ассемблерах и проверял за компилятором коды, но в 1996 году дал себе слово к ассемблеру больше не возвращаться. С тех пор - ни-ни.
Linux Firefox
 Латвия
0
0
Heisenberg2
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Писать на асме и иметь возможность в один клик посмотреть асм с целью дебага - две большие разницы.
Windows Safari Chrome
 Домодедово
2
0
id
В один клик посмотреть асм-выдачу оптимизирующего компилятора? Который вертит регистровым файлом как хочет? В надежде на что? На то, что именно вам повезет найти ошибку в компиляторе, пропущенную комьюнити мощностью во многие миллионы голов?

Каюсь, грешен, сам иногда смотрю. Примерно раз в год. И каждый раз - по разным причинам. Но обычно, если в программе ошибка - это моя ошибка. И обычно это достаточно высокоуровневая ошибка, чтобы суметь разглядеть ее в асм-листинге.
Linux Safari Chrome
 Москва
2
0
LLeo
Эх, где то сладкое время, когда ошибки в программе можно было найти, заглянув в листинг машинных кодов и поняв, что в регистр R5 (годами позже - в аккумулятор) суётся не то число, что надо... Сейчас ошибки в программах такого уровня, что мне все чаще приходится писать специальные _инструменты_, которые всякий раз особым образом (особым именно для данной задачи) анализируют и визуализируют выбранные данные в выбранные моменты. И только тогда и можно найти ошибки в алгоритмах...
Linux Firefox
 Израиль
7
0
Mason
> Лично я зарезал штуки 3 буратинов на техническом интервью за признание, что они юзали сабж для продакшена.

Вот уж действительно, "неизвестно, кому повезло". Интервью - это не только оценка работника, но и оценка работодателя. Упертый работодатель - горе. Для каждой задачи есть свой инструмент. И я бы поостерегся работать в конторе, где оперируют понятиями "это для быдл"
Windows Safari Chrome
 Домодедово
3
0
id
Из фраз "сделать пиздато" и "затруднительно посмотреть листинг после компиляции" следует простой вывод: автор "немного" похож на современного школьника, для которого работа компьютера за пределами вконтактика представляется черной магией, принципиально не поддающейся пониманию.

А на самом деле, Ардуино - это компилятор gcc и набор библиотек. И никакой другой магии там нет. И к ключам компилятора есть полный доступ: пожалуйста, отключайте всю оптимизацию, если это вдруг зачем-то нужно. И библиотечными функциями пользоваться никто не заставляет, дергай железо вволю руками если необходимо. И перехватить прошивку между gcc и avrdude - плевое дело, и пожалуйста, изучайте результат хоть в IDA, хоть в чем.

Да, увы, старые добрые времена понемногу проходят, и школьников, считающих себя программистами - все больше. И это прокатывает, например, в web. Но в embedded это прокатить не может, потому что опций "сделайте канал пошире" и "воткните памяти побольше" здесь нет. И когда адепт "магического устройства компа" вдруг считает себя начальником эмбеддеров, тут уж туши свет и сливай воду, пропал калабуховский дом.
Linux Safari Chrome
 Москва
0
0
LLeo
Смешнее, когда люди, считающие, что грешно писать в Ардуино, пишут программы под Виндоус, и это типа норм.
Windows Safari Chrome
 Израиль
0
0
Mason
Но у этого есть и положительная сторона. Embedded кто-то должен программировать, а эта школота на самом деле ничего не понимает, понимать не хочет и не может. Я сейчас значительно меньше волнуюсь по поводу найти работу, несмотря на приближающийся пенсионный возраст.

Школота пусть чешет свое ЧСВ. Хотя странно, конечно, что ему позволяют интервьюировать кандидатов.
Windows Safari Chrome
 Домодедово
0
0
id
> Embedded кто-то должен программировать, а эта школота на самом деле ничего не понимает, понимать не хочет и не может.

Из этого, увы, не следует "и потому - не делает". К сожалению, делает. Как умеет. И впаривает свои решения на самый верх, потому что лозунг "мы - не быдло, у нас все конкретно!" очень классово близок там. А когда потом случается то, что случается, всегда найдется стрелочник...
Linux Firefox
 Израиль
1
0
Mason
Никакой мистики. Все написано открытым текстом в исходниках.

Посмотри в имплементацию (соседний файл .cpp)
Функция connect копирует полученый параметр в буфер:
177: length = writeString(id,buffer,length);

А функция setServer сохраняет полученый указатель как есть:
649: this->domain = domain;

Очевидно, что если ты передаешь указатель на стек (а в твоем коде переменная URL - автоматическая переменная на стеке), то на выходе из твоей функции строка перетрется.

Я 18 лет не трогал С++, но попробовал бы решить проблему, объявив переменную URL "static" вот так
=====================
static String URL = "tailor.cloudmqtt.com";
=====================
Linux Safari Chrome
 Москва
0
0
LLeo
Ну у меня нет возможности вообще объявлять переменные. В моей задаче никакой MQTT не заработает, пока админ системы в своем скрипте в текстовом файле (или в команде от сервера) не напишет строку вида:
MQTT.connect pool.mqtt.com:12345 ivanpetrov 4fDxRg5
Что будет командой запустить систему MQTT с параметрами url, порт, логин и пароль (если они указаны). Соответственно все переменные система берет из подобных текстовых инструкций, будь то общение mqtt или указание, в кокой gpio записать единицу или приказ исполнить нотную мелодию или mp3 в динамик.
Linux Safari Chrome
 Нидерланды
0
0
Robbot anatoly
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Pakh.
Oslu ponytno.
Linux Ubuntu Firefox
 Германия
0
0
gehrmann
Не решится ли, если завернуть вызов .c_str() в strdup()?
Linux Safari Chrome
 Россия
3
0
Андрей Рогожников
Подозреваю, что функция CF объявлена как
String CF(... variable_name)
и получается следующая цепочка:
* в вызывающей функции есть код примерно такого вида:
client.setServer(CF("url").c_str())
* внутри CF ищется переменная с именем variable_name; эта переменная имеет тип String, как я понимаю из описания выше;
* делается return <имя переменной>;
* в вызывающую функцию отдается объект типа String;
* у этого объекта вызывается метод c_str() (он возвращает указатель);
* в функции setServer() указатель не используется сразу, а запоминается для последующего использования.

А дальше происходит следующее. Объект String, который вернулся из CF(), уничтожается как временный объект, который вышел из зоны видимости. И указатель, который был сохранен при вызове setServer(), оказывается висячим.

Как это исправить с минимальными переделками. Так как я не вижу кода CF(), то предложу решение в более-менее общем виде:
1) объявить CF как const String& CF(const char* var) или const String& CF(const String& var) в зависимости от обстоятельств; самое главное здесь -- возвращаемый тип, const String&; так можно обеспечить, что вернется ссылка на объект, а не временный объект, и память будет указывать на статическую переменную URL;
2) объявить CF как const char* CF(const char* var) или const char* CF(const String& var), а в самой функции возвращать c_str() от найденной переменной; идея здесь та же -- возвращать указатель на статическую память, а не временный объект.

Если исходники не слишком секретные, то могу посмотреть внимательнее.
Linux Safari Chrome
 Россия
0
0
Андрей Рогожников
Временное решение из апдейта будет грязным, но работающим, если new вызывается только один раз. Если new делается каждый вызов, то будет утекать память, но вы и сами, наверное, это понимаете.
Linux Ubuntu Firefox
 Израиль
0
0
200-1.95M
Как C++ что для меня язык сплошной из белых пятен
Так мой стишок загадочен и непонятен
Суразность придаётся в осмысленьи
Полезность обретётся в примененьи
Mac Safari Chrome
 Россия
3
0
boriay
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Раз у вас и так глобальные переменные, переделайте свою функцию, чтобы возвращала ссылку Srting& CF() Или указатель, для верности. Или сразу указатель на строку const char* CF() { return g_cf.c_str(); }
Только аккуратнее, не скопируйте и там.
Windows Safari Chrome
 Израиль
3
0
б
Извините за офтопик. Но не смог сдержаться. Вы здесь с октября 12 года, и это первый ваш комментарий! Причем предельно краткий и без единой лишней буквы.

Готов поспорить, что ваш код - совершенство :)
Снимаю шляпу.
Linux Safari Chrome
 Москва
0
0
xor4xor
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
сделать глобальную переменную
- массив char, копировать в нее данные из string и передавать ее функции из библиотеки.
Linux Firefox
 Израиль
0
0
Mason
> моя функция Srting CF(String имя) ищет "переменную" с этим именем и возвращает ее значение.

Функция с таким прототипом как раз и создает копию на стеке. Возвращай или ссылку (работает, но категорически не рекомендую) или указатель. А еще лучше возвращай сразу указатель на char. То есть

return found_global_string.c_str();

UPD. Обнаружил, что выше тебе написали практически то же самое. Почти дословно. Но удалять не буду :) Повторение - мать учения.
Linux Safari Chrome
 Москва
0
0
LLeo
Самое интересное, что начав добавлять библиотеку MQTT, и ещё до появления проблемы, я подумал, а почему у меня помимо CF() нет ещё и функции, которая бы возвращала не String, а String.c_str() если это стало так часто нужно... И написал ее, назвав CFs(). Но не смог победить ошибки компилятора: что-то ему не нравилось в оформлении функции, и я забил на это...
Linux Firefox
 Израиль
0
0
Mason
[mode zanuda on]
Я давно признал преимущество компилятора над нами, кожаными ублюдками. Поэтому если ему не нравится мой код, это верный знак, что я где-то что-то не понимаю. И стараюсь понять где. А потом можно и выкинуть этот код.

Я против расширения API (интерфейса). Важнее понять, о каком объекте идет речь и где он находится в памяти. Если тебе нужны и CF() и CFs(), то значит тебе не нужен CFs(). Просто возвращай указатель на глобальную строку
String *CF();

[mode zanuda off]
Linux Safari Chrome
 Великобритания
0
0
AlexSa
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
А вот это, кстати, могло режить проблему.
const char* CFs(args)
{
return YourArrayName[yourIndex].c_str();
}

В этом случае, если элементы массива живут вечно, c_str использует потроха из-под переменной типа String, живущей в массиве.
То же самое, но чуть опрятнее, с константной ссылкой

const char* CFs(args)
{
const String& ret = YourArrayName[yourIndex];
return ret.c_str() ;
}

Или то же самое C-style с указателем

const char* CFs(args)
{
const String* ret = & (YourArrayName[yourIndex]) ;
return ret->c_str() ;
}

А вот так - ошибка, возвращается память временной переменной, которая будет удалена при выходе из этого метода:

const char* CFs(args)
{
String ret = YourArrayName[yourIndex];
return ret.c_str() ;
} // undefined behavior

При всем этом в правильных вариантах и память не течёт, но зато когда кто-то поменяет/перезапишет строку в массиве, указатель вернувшийся от c_str станет невалидным, ибо окучивающий его объект исчезнет/поменяется. И может быть весёлый фейерверк незнамо откуда
Linux Ubuntu Firefox
 Гатчина
0
0
Akvilon's
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Всё правильно написано. Время жизни тут не причём. Но надо узнать, что возвращает метод c_str(). Он должен вернуть const char *. С STL всё бы работало именно так, как вы написали.
А что компилятор этого самого ардуино выдаёт как ошибку? Что пишет?
Windows Firefox
 Калуга
0
0
DrGluck
Ардуино же на атмеловских контроллерах работает?
В какой памяти лежит строка, в RAM или в памяти программ?
Потому что ссылка на строку в памяти и ссылка на строку в памяти программ, это две большие разницы.
Хотя вполне допускаю, что ардуинщики о таких вещах вообще не задумываются.
Linux Safari Chrome
 Москва
0
0
LLeo
Нет, в данном случае ESP8266.
Windows Firefox
 Калуга
0
0
DrGluck
Почитал в педевикии про контроллер. Немного удивляет работа с ПЗУ через SPI. А также вот эта странная неопределённость с количеством ОЗУ.
Windows Safari Chrome
 Домодедово
1
0
id
А то, что "взрослые" компы работают с ПЗУ через SPI последние лет 15, вас не удивляет? :)
Тогда удивляйтесь еще больше: ESP32-WROVER работает через SPI с ОЗУ :)
Linux Safari Chrome
 Москва
0
0
LLeo
С точки зрения программирования это нас никак не должно трогать.
Windows Safari Chrome
 Израиль
2
0
б
Ну почему. Один из моих любимых багов в драйвере, которые я решил, был решён благодаря осциллоскопу, подключенному к шине SPI ПЗУ.
Windows Safari Chrome
 Домодедово
0
0
id
Наоборот. Только ардуинщики о таких вещах и задумываются, для прочих "гарвардская архитектура" звучит как заклинание.

А ардуино сейчас работает на всем, от ATTiny до x86. В частности, активно ардуиню на Cortex-M3, но ради дешевого RMII планирую переползать на ESP32.
Windows Safari Chrome
 Австралия
1
0
Иван Миляков
Значит, в Ардуино есть свой класс String? Haha, classic.
std::string кто-нибудь использует, кроме Страуструпа?
Windows Firefox
 Москва
0
1
Naeel
Может быть было бы достаточно явного тайпкаста? типа

client.setServer((char*) URL.c_str(),12399);

Потому что все три сигнатуры функции setServer:

PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {...}
PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {...}
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {...}

имеют в качестве первого аргумента указатель на байт.
Mac Safari
 Санкт-Петербург
0
0
TI_Eugene
Так что в итоге-то? Разобрались/нет?
Пост бы назывался не "грёбаный С++", а "гребаная документация, которую надо читать".

String.c_str() возвращает указатель на результат выполнения функции здесь и сейчас.
А не указатель на некую прибитую гвоздями строку, существующую вечно.

У них так работает:
client.setServer("tailor.cloudmqtt.com",12399);
потому что "..." - константа, вшита в код.
А "String URL = "..." - переменная, живет [в стеке] столько, сколько бог отпустил.
Было бы "const URL..." - совсем другое дело.
Linux Ubuntu Firefox
 Норвегия
1
0
avsmal
> Вот не пойму: с одной стороны все это реально хранится в глобальном пространстве и деться никуда не может. С другой стороны - это лишь некий результат, который вернула некая функция. И компилятор может его не хранить

String CF(String имя)
Возврат из функции CF происходит "по значению". Т.е. возвращается не глобальный объект, а его копия. И время жизни этой копии ограничено строчкой вызова функции. Для того, чтоб это работало, нужно возвращать объект по ссылке и не копировать его по всей цепочке вызовов.
Лучше переделать сигнатуру на такую:
String const& CF(String имя)
А ещё лучше - на такую
String const& CF(String const& имя)
Важно, что в том месте, где в функции CF написан return, возвращать нужно именно глобальный объект. Например, return global_data[i], а не его локальную копию. В том числе это касается и случая, когда соответствующая строка не нашлась. Там, вероятно, возвращается пустая строка. Нужно сделать эту пустую строку глобальной. Для этого можно внутри функции определить её как static
Linux Ubuntu Firefox
 Норвегия
1
0
avsmal
> С другой стороны - это лишь некий результат, который вернула некая функция. И компилятор может его не хранить.
Тут всё даже хуже (лучше). Компилятор в этом случае (возврат объекта по значению) обязан сгенерировать код, который удалит этот объект после завершения выполнения строки кода, в котором происходит вызов функции.
Linux Ubuntu Firefox
 Норвегия
0
0
avsmal
"Дикая конструкция" плоха, т.к. в этом месте
unsigned char* buf = new unsigned char[256];
происходит выделение памяти, которая потом никогда не освобождается.
Соответственно, если таких вызовов в программе много, то может исчерпаться память.
Windows Firefox
 Швейцария
0
0
x386
Этот человек не загрузил свой юзерпик, и я подобрал ему этот. Человек, пишущий такое, должен именно так выглядеть, верно?
Надо на чистом Си писать, а не на плюсах, чтобы был полный контроль над памятью.

всего комментариев: 81

<< предыдущая заметка следующая заметка >>