С Arduino Ethernet shield, каква е разликата между `write()`, `print()` и `printIn()`?

С помощта на Arduino Ethernet Server Library, каква е разликата между:

server.write(data);, server.print(data); и server.println(data);

Знам, че printIn добавя нов ред, където print не. Не мога да намеря примери за server.write();.


person Anonymous Penguin    schedule 26.04.2013    source източник


Отговори (1)


(Дълъг отговор, преминете към TL;DR най-долу, ако е тромав)

Откъде идват print() и write()

За да разберем, можем да погледнем източника. Server е екземпляр на класа EthernetServer, дефиниран в arduino/libraries/Ethernet/EthernetServer.h (само избрани редове)

#include "Server.h"

class EthernetClient;

class EthernetServer : 
public Server {
private:
public:
  virtual size_t write(uint8_t);
  virtual size_t write(const uint8_t *buf, size_t size);
  using Print::write;
};

Добре, значи е Server. Server е дефиниран в /usr/share/arduino/hardware/arduino/cores/arduino/Server.h и има много малко за него:

class Server : public Print {
public:
  virtual void begin() =0;
};

Това означава, че сървърът е подклас на Print, така че можем да търсим разлики между write() и print() там.

print() и write() параметри

Виждаме, че този клас (т.е. Print) дефинира редица претоварени print() методи:

size_t print(const __FlashStringHelper *);
size_t print(const String &);
size_t print(const char[]);
size_t print(char);
size_t print(unsigned char, int = DEC);
size_t print(int, int = DEC);
size_t print(unsigned int, int = DEC);
size_t print(long, int = DEC);
size_t print(unsigned long, int = DEC);
size_t print(double, int = 2);
size_t print(const Printable&);

и три претоварени write() метода:

virtual size_t write(uint8_t) = 0;
size_t write(const char *str) { return write((const uint8_t *)str, strlen(str)); }
virtual size_t write(const uint8_t *buffer, size_t size);

Както можете да видите, C-низът write използва блока write (третият метод), а в изпълнението по подразбиране блоковият запис използва запис на байт (първият метод), който е чист виртуален метод: virtual size_t write(uint8_t) = 0;. Той трябва да бъде заменен във всеки клас, който произлиза от Print. Освен това блокът write() може също да бъде заменен, за да се записват многобайтови данни по-ефективно.

И така, по параметри:

  • write(): върху байтове (uint8_t), буфери за байтове и указатели на масив от знаци (= обикновени C низове)
  • print(): Arduino Strings, ints и longs (в каквато и да е база), floats и всеки клас, получен от Printable, в допълнение към chars и C низове.

Както можете да видите, формално има малко припокриване между параметрите write() и print() вземания. Например само write() взема uint8_t, но само print() може да вземе char. Единствената област на припокриване са низовете в стил C: има print(const char[]); и write(const char *str);. Но дори в случаи като char функцията print() просто извиква write(uint8_t):

size_t Print::print(char c)
{
  return write(c);
}

Същото важи и за print(char[])

write() в `EthernetServer

Класът EthernetServer въвежда метод за блоково записване

size_t EthernetServer::write(const uint8_t *buffer, size_t size) 

и в EthernetServer write(uint8_t) просто прехвърля към блока запис:

size_t EthernetServer::write(uint8_t b) 
{
  return write(&b, 1);
}

Тъй като всички print() повиквания и не-uint8_t write() повиквания използват или write(uint8_t), или write(uint8_t*, size_t), в класа EthernetServer всяко print/write повикване се прави с помощта на запис на блок.

Ефективност и избор между print() и write()

Функциите thunking print() (като print(char c)) най-вероятно ще бъдат вградени от gcc компилатора, но ако се притеснявате за това, можете да извикате write() вместо print().

Един случай, в който може да искате да извикате write() вместо print(), за да спестите няколко тактови цикъла, е когато държите byte/uint8_t и трябва да го отпечатате. С помощта на print() вашите данни ще трябва да бъдат преобразувани в 4-байтова стойност (int) и след това да бъдат отпечатани с помощта на още код. В този случай write() ще бъде малко по-бърз.

От друга страна, последователността на кода вероятно също струва нещо. От тази гледна точка може да има смисъл да се правят всички print() повиквания.

През повечето време обаче вашите типове ще диктуват извикването на функцията print(): write може да приема само три типа вход.


TL;DR: Отговорът на въпроса ви тогава е, че няма голяма разлика между print() и write() освен:

  • Методите write() (байт или блок) са методите, които вършат действителната работа по изпращане на символи някъде, във всеки случай.
  • write() може да приема байтове (uint8_t), байтови буфери и указатели на char array (= обикновени C низове) като параметри, докато print() взема Arduino Strings, ints и longs (в каквато и да е база), floats и всеки клас, извлечен от Printable, в допълнение към chars и C низове. Така че можем да кажем, че write() е по-ниско ниво от print(), предвид факта, че приема само типове от ниско ниво.
  • През повечето време вашите изходни типове ще диктуват кой да използвате. За да направите най-бързия код, използвайте write() за отпечатване на byte/uint8_t типове, но print навсякъде прави кода ви да изглежда малко по-добре IMHO (главно защото не повдига въпросите print() срещу write()).
person angelatlarge    schedule 27.04.2013
comment
Така че защо някой би използвал write()? По-бързо ли е, ако вече използвате типа променлива, който поддържа? - person Anonymous Penguin; 27.04.2013
comment
Благодаря, че го посочихте: това беше грешка в отговора. Опитах се да го отговоря, но това направи отговора още по-дълъг :( Кажете ми, ако това не изясни. - person angelatlarge; 27.04.2013