Работа с шаблонами

ChaNger
Offline
Зарегистрирован: 22.02.2018

Здравствуйте, начла заниматься шаблонами, решил это все засунуть в бибиотеку.

 

В чем суть программы - создать одну строку которая будет выглядить примерно так: &имя_переменной=значение_переменной

Мы создаем структуру который в себе хранит имя и значение, работаем со значением, когда нам нужно собрать строку для отправки мы вызываем функцию класса processingData, которая примет нашу структуру, определит тип значения которое в структуре, и вызовет нужную перегрузку и допишит к имени нужный символ.

Я создал шаблонную структуру, и пытался передать ее в качестве аргумента в функцию класса 

вот код .h файла

#ifndef myLibrarie_h
#pragma once

#if defined(ARDUINO) && ARDUINO >= 100
	#include "Arduino.h"
#else
	#include "WProgram.h"
#endif

template<typename T>struct dataStruct {
	String name;
	T value;
};

class myLibrarie {
	public:
		String data = "&";

		myLibrarie();
		void packetReceive();
		void processingData(dataStruct<T> &variable);
		String toString(String stringValue);
	private:

		void definitionType( int value, String &name);
		void definitionType( float value, String &name);
		void definitionType( bool value, String &name);
		void definitionType( char value, String &name);
};

#endif

вот код .cpp файла

#include "myLibrarie.h"

void myLibrarie::myLibrarie() {
	Serial.begin(9600);
}

void myLibrarie::definitionType( int value, String &name) {
	name += "I";
}
void myLibrarie::definitionType( float value, String &name) {
	name += "F";
}
void myLibrarie::definitionType( bool value, String &name) {
	name += "B";
}
void myLibrarie::definitionType( char value, String &name) {
	name += "C";
}

String myLibrarie::toString(String stringValue) {
	stringValue = String(data, DEC);	
	return stringValue; 
}

void myLibrarie::processingData(dataStruct<T> &variable) {
	data += definitionType(variable.value, variable.name);
}

 

Пытался скомпилировать выдало ошибку

myLibrarie.h:25:34: error: 'T' was not declared in this scope

И 

myLibrarie.h:25:35: error: template argument 1 is invalid

 

После покомпался в интернете и сделал такой код

вот код .h файла

#ifndef myLibrarie_h
#pragma once

#if defined(ARDUINO) && ARDUINO >= 100
	#include "Arduino.h"
#else
	#include "WProgram.h"
#endif

template<typename T>
struct dataStruct {
	String name;
	T value;
};

template<typename T>class myLibrarie {
	public:
		String data = "&";

		myLibrarie();
		void processingData(dataStruct<T> &variable);
		String toString(String stringValue);
	private:
		void definitionType( int value, String &name);
		void definitionType( float value, String &name);
		void definitionType( bool value, String &name);
		void definitionType( char value, String &name);
};

#endif // #ifndef myLibrarie_h

вот код .cpp файла 

#include "myLibrarie.h"

template<typename T>void myLibrarie<T>::myLibrarie() {
	Serial.begin(9600);
}

template<typename T>void myLibrarie<T>::definitionType( int value, String &name) {
	name += "I";
}
template<typename T>void myLibrarie<T>::definitionType( float value, String &name) {
	name += "F";
}
template<typename T>void myLibrarie<T>::definitionType( bool value, String &name) {
	name += "B";
}
template<typename T>void myLibrarie<T>::definitionType( char value, String &name) {
	name += "C";
}

template<typename T>String myLibrarie<T>::toString(String stringValue) {
	stringValue = String(data, DEC);	
	return stringValue; 
}

template<typename T>void myLibrarie<T>::processingData(dataStruct<T> &variable) {
	data += definitionType(variable.value, variable.name);
} 

Это код который я использую в arduino IDE (к первому варианту кода и ко втрому)


#include <myLibrarie.h>

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

}

void loop() {
  String objName = "Hello";
  dataStruct<int> firstObj;
  myLibrarie<int> Obj;

  firstObj.name = objName;
  firstObj.value = 5;

  Obj.processingData(firstObj);
}

И этот код возвращает ошибку: 

/tmp/ccLtigd4.ltrans0.ltrans.o: In function `loop':
/home/~/Arduino/must_be_deleted/must_be_deleted.ino:17: undefined reference to `myLibrarie<int>::toString(String)'
collect2: error: ld returned 1 exit status
exit status 1
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Что у Вас делают определения типа template<typename T> в .cpp файле? Откуда компилятору знать какие именно T Вы подсунете? Это же самостоятельный файл. Он компилируется отдельно от всего остального.

Это делается либо прямо в .h файле, либо в CPP но тогда с конкретными типами.

ChaNger
Offline
Зарегистрирован: 22.02.2018

ЕвгенийП пишет:

Это делается либо прямо в .h файле, либо в CPP но тогда с конкретными типами.

Я убирал 

template<typename T> 

из .cpp файла, но мне нужно использовать структуру которая объявлена в .h как:

template<typename T>struct dataStruct {
	String name;
	T value;
};

Теперь вопрос как подсунуть ему T из .h файла?

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Пишите всё в .h файле. Без .cpp вообще. Шаблонные библиотеки так пишутся чуть реже, чем всегда.

ChaNger
Offline
Зарегистрирован: 22.02.2018

У меня к примеру есть функция которая принимает шаблонную структру

template<typename T>void processingData(dataStruct<T> &variable) {
    data = data + PACKET_BEGIN + definitionType(variable.value, variable.name);
}

Вопрос класстоже должен быть шаблонный и иметь свой тип, я перекинул все в .h файл, если не использовать фунцкию то все ок, но как только я использую функцию мне выдает ошибку

/home/~/Arduino/test/test.ino: In function 'void loop()':
test:13: error: request for member 'processingData' in 'Obj', which is of non-class type 'myLibrarie()'
   Obj.processingData();
       ^
exit status 1
request for member 'processingData' in 'Obj', which is of non-class type 'myLibrarie()'
 
Вот полный код моего .h файла
#ifndef myLibrarie_h
#pragma once

#if defined(ARDUINO) && ARDUINO >= 100
	#include "Arduino.h"
#else
	#include "WProgram.h"
#endif

template<typename T>struct dataStruct {
	String name;
	T value;
};

class myLibrarie {
	public:
		String data;

		myLibrarie() {
			Serial.begin(9600);
		}

		template<typename T>void processingData(dataStruct<T> &variable) {
		  data = data + PACKET_BEGIN + definitionType(variable.value, variable.name);
		}

		template<typename T>String toString(T value, String sendName) {
		  sendName = sendName + '=' + String(value, DEC);  
		  return sendName; 
		}
	private:	
		String definitionType( int value, String name) {
		  name += "I";
		  return toString(value, name);
		}
		String definitionType( float value, String name) {
		  name += "F";
		  return toString(value, name);
		}
		String definitionType( bool value, String name) {
		  name += "B";
		  return toString(value, name);
		}
		String definitionType( char value, String name) {
		  name += "C";
		  return toString(value, name);
		}
};

#endif // #ifndef transportProtokol_h

 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ChaNger пишет:

У меня к примеру есть функция которая принимает шаблонную структру

template<typename T>void processingData(dataStruct<T> &variable) {
    data = data + PACKET_BEGIN + definitionType(variable.value, variable.name);
}

Вопрос класстоже должен быть шаблонный и иметь свой тип?

Не могу сказать, чтобы я понял вопрос, но ... тот факт, что где-то используется шаблон не накладвает никаких требований на использующие его конструкции. Никаких. Если Вам надо её использовать - используйте и не парьтесь. Например, если Вы пользуетесь EEPROM, Вы наверняка пользовались функциями EEPROM.put и EEPROM.get. А Вы знаете как они написаны?

    template< typename T > T &get( int idx, T &t ){
        EEPtr e = idx;
        uint8_t *ptr = (uint8_t*) &t;
        for( int count = sizeof(T) ; count ; --count, ++e )  *ptr++ = *e;
        return t;
    }
    
    template< typename T > const T &put( int idx, const T &t ){
        EEPtr e = idx;
        const uint8_t *ptr = (const uint8_t*) &t;
        for( int count = sizeof(T) ; count ; --count, ++e )  (*e).update( *ptr++ );
        return t;
    }

И ничего. Народ пользует, даже не догадываясь, что там шаблоны.

Шаблон же это просто подсказка компилятору как создать функцию для нужного типа и не более того.  Не особой разницы написать несколько функций с одним именем для разных типов и одну шаблонную.

Вы бы сделали маленький пример (совсем маленький) и отладилиь бы не нём

ChaNger
Offline
Зарегистрирован: 22.02.2018

Я написал шаблонные функции которые работают вне какого-либо класса, и все ок. Они принимают любые типы данных и все прекрасно работает, но как только я пытаюсь сделать тоже с классом выдает ту ошибку.

Есть пример в интернете только для Visual Studio

#include "stdafx.h"
#include <iostream>
 
using namespace std;
 
#include <iomanip>
 
template <typename T>
class Stack
{
private:
    T *stackPtr; // указатель на стек
    int size; // размер стека
    T top; // вершина стека
public:
    Stack(int = 10);// по умолчанию размер стека равен 10 элементам
    ~Stack(); // деструктор
    bool push(const T  ); // поместить элемент в стек
    bool pop(); // удалить из стека элемент
    void printStack();
};
 
int main()
{
    Stack <int> myStack(5);
 
    // заполняем стек
    cout << "Заталкиваем элементы в стек: ";
    int ct = 0;
    while (ct++ != 5)
    {
        int temp;
        cin >> temp;
        myStack.push(temp);
    }
 
    myStack.printStack(); // вывод стека на экран
 
    cout << "\nУдаляем два элемента из стека:\n";
 
    myStack.pop(); // удаляем элемент из стека
    myStack.pop(); // удаляем элемент из стека
    myStack.printStack(); // вывод стека на экран
 
    return 0;
}
 
// конструктор
template <typename T>
Stack<T>::Stack(int s)
{
    size = s > 0 ? s: 10;   // инициализировать размер стека
    stackPtr = new T[size]; // выделить память под стек
    top = -1; // значение -1 говорит о том, что стек пуст
}
 
// деструктор
template <typename T>
Stack<T>::~Stack()
{
    delete [] stackPtr; // удаляем стек
}
 
// элемент функция класса  Stack для помещения элемента в стек
// возвращаемое значение - true, операция успешно завершена
//                                    false, элемент в стек не добавлен
template <typename T>
bool Stack<T>::push(const T value)
{
    if (top == size - 1)
        return false; // стек полон
 
    top++;
    stackPtr[top] = value; // помещаем элемент в стек
 
    return true; // успешное выполнение операции
}
 
// элемент функция класса  Stack для удаления элемента из стек
// возвращаемое значение - true, операция успешно завершена
//                                    false, стек пуст
template <typename T>
bool Stack<T>::pop()
{
    if (top == - 1)
        return false; // стек пуст
 
    stackPtr[top] = 0; // удаляем элемент из стека
    top--;
 
    return true; // успешное выполнение операции
}
 
// вывод стека на экран
template <typename T>
void Stack<T>::printStack()
{
    for (int ix = size -1; ix >= 0; ix--)
        cout << "|" << setw(4) << stackPtr[ix] << endl;
}

Но на арудино он не работает.

P.S cout я удалял, в функции loop я только создал объект класса, и пытался вызвать функцию

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

если у тебя нет диплома доктора философии, шаблоны классов на Ардуне ты никада не освоишь. 

Зато потом те, кто с такими дипломами, да шаблоны освоили, за 50 тыщ даже с дивана не встають. 

Думай. 

ChaNger
Offline
Зарегистрирован: 22.02.2018

Просто смысл в том что на десктопе они работают, а на Ардуине нет

sadman41
Offline
Зарегистрирован: 19.10.2016

Так на ардуине Си нечестный. Он даже знатоков двадцати языков облапошивает как детей малых.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

ChaNger пишет:
Просто смысл в том что на десктопе они работают, а на Ардуине нет
И в Ардине они работают. Вот только у тех у кого ....  не работают. Исправьте этот недостаток. И не надо тырить код отсюда http://cppstudio.com/post/5188/

Научитесь сначало правильно создавать классы , и только потом лезте к шаблонам классов.

/**/
//-------------------------------
class Cl_Stack {
  protected:
    byte *ptr;
    byte size;
    int top;
  public:
    Cl_Stack(int s = 10);
    ~Cl_Stack();
    bool push(byte num);
    bool pop(byte & num);
};
Cl_Stack::Cl_Stack(int s) {
  size = s > 0 ? s : 10;  // инициализировать размер стека
  ptr = new byte[size]; // выделить память под стек
  top = -1; // значение -1 говорит о том, что стек пуст
}
Cl_Stack::~Cl_Stack() {
  delete [] ptr;
}
bool Cl_Stack::push(byte num) {
  if (top > size) return 0;
  top++;
  ptr[top] = num;
  return 1;
}
bool Cl_Stack::pop(byte & num) {
  if (top < 0) return 0;
  num = ptr[top];
  top--;
  return 1;
}
//-------------------------------
const byte len = 5;
Cl_Stack Stack(len);
byte num;
//-------------------------------
void setup() {
  Serial.begin(9600);
  for (int i = 0; i < len; i++) {
    Stack.push(i);
  }
  for (int i = 0; i < len; i++) {
    Stack.pop(num);
    Serial.print(" ");
    Serial.print(num);
  }
  Serial.println();
}
void loop() {
}
/*Скетч использует 2392 байт (7%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 204 байт (9%) динамической памяти, оставляя 1844 байт для локальных переменных. Максимум: 2048 байт.
*/

 И тоже самое через шаблоны 

/**/
//-------------------------------
template <typename T> class Cl_Stack {
  protected:
    T *ptr;
    byte size;
    int top;
  public:
    Cl_Stack(int s = 10);
    ~Cl_Stack();
    bool push(T num);
    bool pop(T & num);
};
template <typename T> Cl_Stack<T>::Cl_Stack(int s) {
  size = s > 0 ? s : 10;  // инициализировать размер стека
  ptr = new T[size]; // выделить память под стек
  top = -1; // значение -1 говорит о том, что стек пуст
}
template <typename T> Cl_Stack<T>::~Cl_Stack() {
  delete[] ptr;
}
template <typename T> bool Cl_Stack<T>::push(T num) {
  if (top > size) return 0;
  top++;
  ptr[top] = num;
  return 1;
}
template <typename T> bool Cl_Stack<T>::pop(T & num) {
  if (top < 0) return 0;
  num = ptr[top];
  top--;
  return 1;
}
//-------------------------------
const byte len = 5;
Cl_Stack<byte> Stack(len);
byte num;
//-------------------------------
void setup() {
  Serial.begin(9600);
  for (int i = 0; i < len; i++) {
    Stack.push(i);
  }
 for (int i = 0; i < len; i++) {
    Stack.pop(num);
    Serial.print(" ");
    Serial.print(num);
  }
  Serial.println();
}
void loop() {
}
/*Скетч использует 2392 байт (7%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 204 байт (9%) динамической памяти, оставляя 1844 байт для локальных переменных. Максимум: 2048 байт.
*/

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ChaNger пишет:
Но на арудино он не работает.

P.S cout я удалял, в функции loop я только создал объект класса, и пытался вызвать функцию

Что Вы там удаляли? Что создавали? А главное, как?

Если Вы хотите подсказки по неработающему коду, так и публикуйте его, а не тот, который "на десктопе работает".

ChaNger
Offline
Зарегистрирован: 22.02.2018

С http://cppstudio.com/post/5188/ я взял просто 100% работающий пример, чтобы проверит будет ли он работать у меня

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Понятно. Значит Вашего кода, который Вы запускали, мы так и не увидим. 

ChaNger
Offline
Зарегистрирован: 22.02.2018

Этот код Ардуино я использовал для своего кода

dataStruct<int> firstObj;
myLibrarie Obj;

void setup() {

}

void loop() {
     processingData( firstObj );
}

Это код к коду с cppstudio

#include myLibrarie

Stack <int> myStack(5);

void setup() {

}

void loop() {
    myStack.pop();
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ChaNger пишет:

Этот код Ардуино я использовал для своего кода

Да, нет, не этот. Тут по меньшей мере не хватает описания dataStruct и myLibrarie. Может они в отдельном файле, но тогда всё равно не хватает include. Даже если Вы его где-то 100500 постов назад и выкладывали - это ничего не меняет. Никто не будет искать что-то и думать то ли Вы его с тех пор меняли, то ли нет.

ChaNger пишет:

Это код к коду с cppstudio

Вот на это глубоко наплевать. Больше не постите.

ChaNger

послушайте, если Вам действительно нужна помощь (пока ещё хоть кому-то не надоело пытаться Вам помочь), Вы пожалуйста, не умничайте, не отбрасывайте то, что по-Вашему правильно, не публикуйте ХЗ каких кодов с ХЗ каких сайтов. Просто возмите Ваш код полностью, так чтобы его можно было просто сохранить и запустить. Выложите его и объясните толком в чём у Вас проблема.

 

ChaNger
Offline
Зарегистрирован: 22.02.2018

Я Ваc понял вот это полный код .h файла

#ifndef myLibrarie_h
#pragma once

#define PACKET_BEGIN '&'

#if defined(ARDUINO) && ARDUINO >= 100
	#include "Arduino.h"
#else
	#include "WProgram.h"
#endif

// Эту структуру нужно передать в функцию, которая находится в классе
template<typename structT>struct dataStruct {
	String name;
	structT value;
};

template<typename T>class myLibrarie {
	String data;
	
	// Конструктор
	myLibrarie() {
		Serial.begin(9600);
	}

	void processingData(dataStruct<T> &variable); // Вот в эту функцию
	String toString(T value, String sendName);    // Это вспомогательная функция
	String definitionType(int value, String name); // Это вспомогательная функция
	String definitionType(float value, String name); // Это вспомогательная функция
	String definitionType(bool value, String name); // Это вспомогательная функция
	String definitionType(char value, String name); // Это вспомогательная функция
};

template<typename T> void myLibrarie<T>::processingData(dataStruct<T> &variable) {
  	data = data + PACKET_BEGIN + definitionType(variable.value, variable.name);
}

template<typename T>String myLibrarie<T>::toString(T value, String sendName) {
  	sendName = sendName + '=' + String(value, DEC);  
  	return sendName; 
}

template<typename T>String myLibrarie<T>::definitionType( int value, String name) {
  	name += "I";
  	return toString(value, name);
}
template<typename T>String myLibrarie<T>::definitionType( float value, String name) {
  	name += "F";
  	return toString(value, name);
}
template<typename T>String myLibrarie<T>::definitionType( bool value, String name) {
	name += "B";
  	return toString(value, name);
}
template<typename T>String myLibrarie<T>::definitionType( char value, String name) {
  	name += "C";
  	return toString(value, name);
}

#endif

Вот это код Ардуино

#include <myLibrarie.h>

myLibrarie<int> Obj();
dataStruct<int> structObj;

void setup() {
  Serial.begin(9600);
}

void loop() {
  Obj.processingData(structObj); // Тут я хочу передать объект структуры
}

 И компилятор выдает ошибку

/home/~/Arduino/test/test.ino: In function 'void loop()':
test:11: error: request for member 'processingData' in 'Obj', which is of non-class type 'myLibrarie<int>()'
   Obj.processingData(structObj);
       ^
exit status 1
request for member 'processingData' in 'Obj', which is of non-class type 'myLibrarie<int>()'
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, эта ошибка уйдёт сразу, как только Вы правильно объявите переменную Obj, а именно, либо уберёте скобки в строке №3 (просто уберите нафиг), либо уж напишете их правильно. лучше просто убрать.

Этой ошибки больше не будет, но появится новая. Дело в том. что у Вас в классе все методы private, а Вы пытаетесь обращаться к ним снаружи. Вставьте в строку 20 текст "public:" (без кавычек, конечно).

После этого всё начнёт нормально компилироваться, но это не значит, что ошибок больше нет. Но, сначала скомпилируйте и посмотрите, что получается.

Обратите внимание, ни одна из двух ошибок, о которых я говорю никак не связана с шаблонами. Это касается абсолютно любых классов.

ChaNger
Offline
Зарегистрирован: 22.02.2018

Да работает, спасибо.

Но у меня остался вопрос:

Я создаю объект класса и структуры

myLibrarie<int> Obj;
dataStruct<int> structObj;

Они оба имеют тип Int

Но когда я пытаюсь передать в функицю структуру с типом данных float выдает ошибку и впринципе логично

#include <myLibrarie.h>

myLibrarie<int> Obj;
dataStruct<float> structObj;

void setup() {
  
}

void loop() {
  Obj.processingData(structObj);
}

Но как мне сделать чтобы функция могла принимать любые структуры, я так делал с функциями и все работало (функция была вне какого-либо класса и структуры, и могла принимать любые типы структур)

template<typename T>void processingData(dataStruct<T> &variable) {
  data = data + PACKET_BEGIN + definitionType(variable.value, variable.name);
}
ChaNger
Offline
Зарегистрирован: 22.02.2018

У меня получилось 

#ifndef myLibrarie_h
#pragma once

#define PACKET_BEGIN '&'

#if defined(ARDUINO) && ARDUINO >= 100
	#include "Arduino.h"
#else
	#include "WProgram.h"
#endif

// Эту структуру нужно передать в функцию, которая находится в классе
template<typename structT>struct dataStruct {
	String name;
	structT value;
};

template<typename T>class myLibrarie {
public:	
	String data;
	
	// Конструктор
	myLibrarie();

	template<typename structT>void processingData(dataStruct<structT> &variable) {
		data = data + PACKET_BEGIN;
	}

	String toString(T value, String sendName);    // Это вспомогательная функция
	String definitionType(int value, String name); // Это вспомогательная функция
	String definitionType(float value, String name); // Это вспомогательная функция
	String definitionType(bool value, String name); // Это вспомогательная функция
	String definitionType(char value, String name); // Это вспомогательная функция
};

template<typename T> myLibrarie<T>::myLibrarie() {
	Serial.begin(9600);
}

/*template<typename T> void myLibrarie<T>::processingData(dataStruct<structT> &variable) {
  	data = data + PACKET_BEGIN;
}*/

template<typename T>String myLibrarie<T>::toString(T value, String sendName) {
  	sendName = sendName + '=' + String(value, DEC);  
  	return sendName; 
}

template<typename T>String myLibrarie<T>::definitionType( int value, String name) {
  	name += "I";
  	return toString(value, name);
}
template<typename T>String myLibrarie<T>::definitionType( float value, String name) {
  	name += "F";
  	return toString(value, name);
}
template<typename T>String myLibrarie<T>::definitionType( bool value, String name) {
	name += "B";
  	return toString(value, name);
}
template<typename T>String myLibrarie<T>::definitionType( char value, String name) {
  	name += "C";
  	return toString(value, name);
}

#endif

Но как теперь правильно саму функцию вне класса тоесть вот это:  

template<typename T> void myLibrarie<T>::processingData(dataStruct<structT> &variable) {
  	data = data + PACKET_BEGIN;
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вы сделали свой класс myLibrarie шаблонным, т.е. явно зависящим от типа, так что не жалуйтесь.

Если Вам надо, чтобы сам по себе класс myLibrarie от типа не зависел, а только одна функция была шаблонной, так Вы так и пишите. Класс - без шаблона, а одна (или несколько) функций с шаблонами. Кто Вам мешает-то?

Пример как делать нешаблонный класс с шаблонными методами, можете посмотреть в библиотеке EEPROM. Там класс EEPROMClass нешаблонный, а его методы put и get - шаблонные. Вот также и делайте.

sadman41
Offline
Зарегистрирован: 19.10.2016

Раз сегодня пятница, позвольте и мне набросить... 

Значится, самоучебная задача такая: для массива {typedef enum, char*} сочинить два шаблона функций поиска - того и другого. Т.е. искать строку, соответствующую enum и enum, соотвествующий строке. 

С первым (enum -> string) я более-менее справился (хотя и читал в интернетах, что надёжнее передавать в функцию размер массива самостоятельно, а не доверять компилятору). Но вот второе поставило меня в тупик. Сначала столкнулся с передачей строк в шаблон. Грязно захачил. Затем на моём пути встал возврат результата пользовательского типа... Тут уже без пинка не получается. Пнёте?

templates_test.ino

#pragma GCC optimize ("O0")
#include "templates.h"

typedef enum { diMonday, diTuesday, diWednesday, diThursday, diFriday, diSaturday, diSunday } dayId_t;
typedef enum { ciViolet, ciIndigo, ciBlue, ciGreen, ciYellow, ciOrange, ciRed } colorId_t;

const char name_DAY_MONDAY[]               = "Monday";
const char name_DAY_TUESDAY[]              = "Tuesday";
const char name_DAY_WEDNESDAY[]            = "Wednesday";
const char name_DAY_THURSDAY[]             = "Thursday";
const char name_DAY_FRIDAY[]               = "Friday";
const char name_DAY_SATURDAY[]             = "Saturday";
const char name_DAY_SUNDAY[]               = "Sunday";

const char name_COLOR_VIOLET[]             = "Violet";
const char name_COLOR_INDIGO[]             = "Indigo";
const char name_COLOR_BLUE[]               = "Blue";
const char name_COLOR_GREEN[]              = "Green";
const char name_COLOR_YELLOW[]             = "Yellow";
const char name_COLOR_ORANGE[]             = "Orange";
const char name_COLOR_RED[]                = "Red";

typedef struct {
  uint8_t idx;
  const char* name;
  dayId_t id;
} dayOfWeek_t;

typedef struct {
  uint8_t idx;
  const char* name;
  colorId_t id;
} colorOfRainbow_t;

dayOfWeek_t daysOfWeek[]           = {
  { 1, name_DAY_MONDAY,    diMonday},
  { 2, name_DAY_TUESDAY,   diTuesday},
  { 3, name_DAY_WEDNESDAY, diWednesday},
  { 4, name_DAY_THURSDAY,  diThursday},
  { 5, name_DAY_FRIDAY,    diFriday},
  { 6, name_DAY_SATURDAY,  diSaturday},
  { 7, name_DAY_SUNDAY,    diSunday}
};

colorOfRainbow_t colorsOfRainbow[] = {
  { 1, name_COLOR_VIOLET, ciViolet},
  { 2, name_COLOR_INDIGO, ciIndigo},
  { 3, name_COLOR_BLUE,   ciBlue},
  { 4, name_COLOR_GREEN,  ciGreen},
  { 5, name_COLOR_YELLOW, ciYellow},
  { 6, name_COLOR_ORANGE, ciOrange},
  { 7, name_COLOR_RED,    ciRed}
};

void setup() {
  Serial.begin(115200);
  dayId_t needToReach = diFriday;
  colorId_t needToWear = ciBlue;
  char someday[] = "Monday";

  // 'enumed' ID to char*
  Serial.print("Need to reach: "); Serial.println(getNameById(daysOfWeek, needToReach));
  Serial.print("Need to wear: "); Serial.println(getNameById(colorsOfRainbow, needToWear));

  // char* to 'enumed' ID
  // It's temporary invalid conversion action
  needToReach = getIdByName(daysOfWeek, someday);
  Serial.print(someday); Serial.print(" id: "); Serial.println(needToReach);
}

void loop() { }

templates.h

#define arraySize(_array) ( sizeof(_array) / sizeof(*(_array)) )

//***********************************************************************
template < typename ITEM_TYPE, size_t ARRAY_SIZE, size_t INAME_SIZE >
uint8_t getIdByName(const ITEM_TYPE (&_refArray)[ARRAY_SIZE], char (&_itemName)[INAME_SIZE]) {
  //ID_TYPE ptrItemId = NULL;
  char* _ptrItemName = (char*) &_itemName[0];
  for (uint8_t i = 0x00; i < ARRAY_SIZE; i++) {
    //Serial.print("["); Serial.print(i); Serial.print("]"); Serial.println(_refArray[i].name);
    // strcmp_P() will be here later
    if (!strcmp(_ptrItemName, _refArray[i].name)) {
      //  ptrItemId = _refArray[i].id;
      break;
    }
  }
  //return ptrItemId;
  return false;
}

//***********************************************************************
template < typename ITEM_TYPE, typename ID_TYPE, size_t ARRAY_SIZE>
const char* getNameById(const ITEM_TYPE (&_refArray)[ARRAY_SIZE], const ID_TYPE _itemId) {
  const char* ptrItemName = NULL;
  ID_TYPE currentItemId;
  for (uint8_t i = 0x00; i < ARRAY_SIZE; i++) {
    // Serial.print("["); Serial.print(i); Serial.print("]"); Serial.println(_refArray[i].name);
    // memcpy_P() will be here later
    memcpy(&currentItemId, &(_refArray[i].id), sizeof(ID_TYPE));
    if (_itemId == currentItemId) {
      ptrItemName = _refArray[i].name;
      break;
    }
  }
  return ptrItemName;
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

sadman41 пишет:
читал в интернетах, что надёжнее передавать в функцию размер массива самостоятельно, а не доверять компилятору

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

Уверен, что Вы сами поймёте почему так делать не стоит, я только "пинок" дам. Но если вопросы останутся, спрашивайте.

Итак, "пинок".

Скомпилируйте Вашу программу как есть. А потом, удалите строку №52 и, соответственно, конечную запятую в строке 51 и скомпилируйте снова. Сравните объём полувшегося кода в первом и втором случае. Именно кода, Бог с ними - с данными. Если вопросы останутся, то поэкспериментируйте с различными размерами массивов.

sadman41
Offline
Зарегистрирован: 19.10.2016

Да, я уже без компилирования понимаю, что на выходе появится стотыщмильёнов сгенерированных функций, каждая под свою размерность массива (массивов). Позаимствованная под вечер книга Х.М.Дейтела рассказала, что template это развитие идеи клонирования через #define, а медитация и мерное раскачивание вместе с согражданами в очередном сеансе любви к родине через пользование общественным транспортом, расставило всё на свои места. Надеюсь. Вобщем-то изначально я действовал через указатели и внешний arraySize вычислитель, но потом захотелось красоты. Так что советчик из интернетов не виноват.

Таким образом, как я понимаю, правильный шаблон функции enum => char* будет выглядеть так:

template < typename ITEM_TYPE, typename ID_TYPE>
const char* getNameById(const ITEM_TYPE & _refArray, const ID_TYPE _itemId, size_t _refArraySize) {
  const char* ptrItemName = NULL;
  ID_TYPE currentItemId;
  for (uint8_t i = 0x00; i < _refArraySize; i++) {
    // Serial.print("["); Serial.print(i); Serial.print("]"); Serial.println(_refArray[i].name);
    // memcpy_P() will be here later
    memcpy(&currentItemId, &(_refArray[i].id), sizeof(ID_TYPE));
    if (_itemId == currentItemId) {
      ptrItemName = _refArray[i].name;
      break;
    }
  }
  return ptrItemName;
}

А вызов этак:

  Serial.print("Need to reach: "); Serial.println(getNameById(daysOfWeek, needToReach, arraySize(daysOfWeek)));
  Serial.print("Need to wear: "); Serial.println(getNameById(colorsOfRainbow, needToWear, arraySize(colorsOfRainbow)));

Но, вот как заведести во вторую char* и вывести enum, не являющийся типом аргумента, я так и не понял пока. В книжке лихо оперируют возвращаемыми типами, которые так же применяются и для аргументов, таким образом "проявляясь" в итоговой функции. А тут надо как-то намекнуть компилятору, что следует возвратить. Я пытался, но он говорил, что у него с дедукцией проблема и он ничего делать не будет поэтому. Да и со строковыми параметрами, как я понял, у механизма шаблонизации какая-то идеологическая несовместимость... 

Есть ли выход (а вернее - вход)?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

sadman41 пишет:
Есть ли выход (а вернее - вход)?
Я программирую в среде Ардино. Я с радостью бы и дальше использовал enum-ы . Но как появляются шаблоны в большом количестве enum-ы начинают дурить. Так что пользуйся по старинке . const byte объявляй нужные enum-ы и возращай byte -ы 

const byte one = 0;
const byte two = 1;
const byte tree = 2;

byte func() {
  return one;
}

Неудобно, а шо делать. Ну или так если кого-то очень сильно корежит.

typedef byte enum_t;

const enum_t one = 0;
const enum_t two = 1;
const enum_t tree = 2;

enum_t func() {
  return one;
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

Но для шаблонов нужно, чтобы все допустимые типы поддерживали одинаковые операции. Поэтому первно, что нужно сделать - это класс-обёртку для char *. Класс ни о чём - ему надо иметь конструкторы от char * и const char *, преобразование к char * и обязательно операции сравнения > < и т.п.

Далее, в Вашу структуру нужно обавить акцессоры - функции, который возвращают элемент структуры.

Тогда всё станет однородным и Вы сможете не только затемплатить поиск по люому полю, но и даже праделить класс"массив стуктур" и у него операцию взятия индекса.

sadman41
Offline
Зарегистрирован: 19.10.2016

ЕвгенийП пишет:

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

Прямо вот так, чтобы всегда в обе стороны мапить - не всегда надо, но строку в enum хотелось бы. Например при разборе URL имя ресурса строковое получаем, но хранить строку в памяти - тупо. Еnum удобней. При генерации ответной страницы неплохо с той же структуры дёрнуть что-то связанное с текущим URI, таскаемым по рантайму enum-ом. Или хранить соответствие "состояние сервиса - описание - html class ". И в PROGMEM уложить этот "справочник" Такая задумка, вобщем.

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

sadman41 пишет:
Да, я уже без компилирования понимаю, что на выходе появится стотыщмильёнов сгенерированных функций, каждая под свою размерность массива (массивов).
Ну не так уж и много, ровно столько, сколько сколько типов используется ;)
Ну вот например простой шаблон массива.

template<class Type>
class MyArray {

public:

	/**
	 * Конструктор с параметрами резервирования заданного количества элементов.
	 * @param count [in] - размер массива.
	 * @param block [in] - количество элементов, которое будет резервировать память за раз.
	 */
	MyArray(int count = 0, int block = 8) : portion(block <= 0 ? 8 : block) {
		this->cnt = 0;
		this->reserve = 0;
		this->data = 0;
		if ( count > 0 && resize(count) > 0 ) {
			this->cnt = count;
		}
	}

	/**
	 * Конструктор копирования.
	 * @param org [in] - ссылка на исходный массив.
	 */
	MyArray(const MyArray& org) : portion(org.portion) {
		this->cnt = 0;
		this->reserve = 0;
		this->data = 0;
		this->operator = (org);
	}

	/**
	 * Оператор присваивания.
	 * @return ссылка на собственный экземпляр.
	 * @param rhs [in] - ссылка на исходный массив.
	 */
	MyArray& operator = (const MyArray& rhs) {
		resize(rhs.getCount());
		this->cnt = rhs.getCount();
		for (int index = 0 ; index < this->cnt ; index++) {
			this->data[index] = rhs[index];
		}
		return *this;
	}

	/**
	 * Деструктор
	 */
	virtual ~MyArray() {
		remove();
	}

	/**
	 * Установка актуального размера массива.
	 * @return актуальный размер массива или -1, если не удалось выделить память.
	 * @param newCount [in] - под сколько элементов выделить или перераспределить буфер.
	 */
	int setCount(int newCount) {
		if (resize(newCount) >= 0) {
			this->cnt = newCount > 0 ? newCount : 0;
			return this->cnt;
		}
		return -1;
	}

	/**
	 * Возвращение количества элементов в массиве.
	 * @return количество элементов в массиве.
	 */
	int getCount() const {
		return this->cnt;
	}

	/**
	 * Проверка наличия элементов в массиве.
	 * @return true, если список пустой и false, в противном случае.
	 */
	bool empty() const {
		return (data == 0 || this->cnt == 0);
	}

	/**
	 * Индексный оператор.
	 * @return ссылка на элемент с индексом index или ссылка на элемент, построенный по нулевому адресу, если в массиве отсутствует элемент с индексом index.
	 * @param index [in] - индекс элемента.
	 */
	Type& operator [] (const int index) const {
		if ((unsigned int)index >= (unsigned int)this->cnt) {
			void* address = 0;
			return *((Type*)address);
		}
		return this->data[index];
	}

	/**
	 * Удаление всех элементов массива.
	 */
	void remove() {
		if (this->data) {
			resize(0);
		}
	}

	/**
	 * Вставка нового элемента в массив по заданному индексу.
	 * Индексация производится, начиная с нуля.
	 * @return индекс вставленного элемента или -1, если вставка не возможна.
	 * @param index [in] - индекс, куда необходимо вставить элемент.
	 * @param element [in] - вставляемый элемент.
	 */
	int insert(int index, const Type& element) {
		if ((unsigned int)index > (unsigned int)this->cnt || resize(this->cnt + 1) <= 0) {
			return -1;
		}
		for (int indexNext = this->cnt - 1 ; indexNext >= index ; indexNext--) {
			this->data[indexNext + 1] = this->data[indexNext];
		}
		this->data[index] = element;
		this->cnt++;
		return index;
	}

	/**
	 * Удаление одного или нескольких элементов из массива.
	 * @return результат выполнения функции, true если операция выполнилась успешно и false в противном случае.
	 * @param index [in] - индекс удаляемого элемента.
	 * @param amount [in] - количество удаляемых элементов, начиная с элемента под индексом index.
	 *    Если -1, то удаляются все элементы, начиная с index, до конца массива.
	 */
	bool remove(int index, int amount = 1) {
		if ((unsigned int)index >= (unsigned int)this->cnt || amount == 0) {
			return false;
		}
		if (amount < 0) {
			resize(index);
		} else {
			for (; index + amount < this->cnt ; index++) {
				this->data[index] = this->data[index + amount];
			}
			resize(index);
		}
		return true;
	}

	/**
	 * Производит поиск элемента в массиве.
	 * @return индекс найденного элемента или -1, если элемент не найден.
	 * @param array [in] - ссылка на массив, в котором производится поиск.
	 * @param sought [in] - искомый элемент.
	 * @param start [in] - индекс элемента, с которого необходимо начать поиск.
	 */
	static int indexOf(const MyArray& array, const Type& sought, const int& start = 0) {
		if (start >= array.cnt) {
			return -1;
		}
		for (int index = (start < 0 ? 0 : start) ; index < array.cnt ; index++) {
			if (sought == array.data[index]) {
				return index;
			}
		}
		return -1;
	}

	/**
	 * Оператор добавления элемента в конец массива.
	 * Данная функция увеличивает зарезервированный размер буфера, в отличии от insert.
	 * @return индекс нового элемента.
	 * @param element [in] - добавляемый элемент.
	 */
	int operator += (const Type& element) {
		if (this->cnt >= this->reserve) {
			if (resize(this->cnt + 1) <= 0) {
				return -1;
			}
		}
		this->data[this->cnt] = element;
		return this->cnt++;
	}

private:

	Type* data;
	int cnt;
	int reserve;
	const int portion;

	int resize(int newSize) {
		int portionCount = 0;
		if (newSize > this->reserve) {
			portionCount = 1 + (int)(newSize / this->portion);
			this->reserve = portionCount * this->portion;
		} else {
			portionCount = newSize > 0 ? (int)((this->reserve - newSize) / this->portion) :
					(int)(this->reserve/this->portion);
			this->reserve -= portionCount * this->portion;
		}
		if ( this->cnt > newSize ) {
			this->cnt = (newSize > 0 ? newSize : 0);
		}
		Type* newData = 0;
		if (portionCount) {
			if (this->reserve) {
				newData = new Type[this->reserve];
			}
			if (this->data && newData ) {
				for (int num = 0 ; num < this->cnt ; num++) {
					newData[num] = this->data[num];
				}
			}
			if ( this->data ) {
				delete [] this->data;
			}
			this->data = newData;
		}
		return this->reserve;
	}
};

Объявляем енам, класс и массивы для трех типов.

enum class DayId {Undefined = -1, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};

class DayOfWeek {
public:
  DayOfWeek(DayId id = DayId::Undefined) {
  this->id = (id > DayId::Undefined && id <= DayId::Saturday) ? id : DayId::Undefined;
    this->name = this->id == DayId::Saturday ? "Saturday" :
    this->id == DayId::Monday ? "Monday" :
    this->id == DayId::Tuesday ? "Tuesday" :
    this->id == DayId::Wednesday ? "Wednesday" :
    this->id == DayId::Thursday ? "Thursday" :
    this->id == DayId::Friday ? "Friday" :
    this->id == DayId::Saturday ? "Saturday" :
    "Undefined";
  }
  bool operator == (const DayOfWeek& rhs) const {
    return (rhs.id == this->id) ? true : false;
  }
  const char* name;
  DayId id;
};

MyArray<DayOfWeek> daysOfWeek;
MyArray<String> someStrings;
MyArray<int> someIntegers;

void setup() {
  Serial.begin(9600);
  Serial.println();
  Serial.println();

  daysOfWeek += DayOfWeek(DayId::Wednesday);
  daysOfWeek += DayOfWeek(DayId::Sunday);
  daysOfWeek += DayOfWeek(DayId::Thursday);
  daysOfWeek += DayOfWeek(DayId::Friday);
  daysOfWeek += DayOfWeek(DayId::Monday);
  daysOfWeek += DayOfWeek(DayId::Saturday);
  daysOfWeek += DayOfWeek(DayId::Tuesday);
   int index = MyArray<DayOfWeek>::indexOf(daysOfWeek, DayId(6));
  Serial.print("Index of days: ");
  Serial.println(index);

  someStrings += "cognac";
  someStrings += "vodka";
  someStrings += "tequila";
  index = MyArray<String>::indexOf(someStrings, "vodka");
  Serial.print("Index of strings: ");
  Serial.println(index);

  someIntegers += 73;
  someIntegers += 88;
  someIntegers += 99;
  index = MyArray<int>::indexOf(someIntegers, 73);
  Serial.print("Index of integers: ");
  Serial.println(index);
}

void loop() {}

Должна быть прстроена реализация только для типов DayOfWeek String и int.

Вот вывод поиска.

Index of days: 5
Index of strings: 1
Index of integers: 0

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Микроконтроллеры, которые плохо вели себя в прошлой жизни, в этой получают прошивку на C++. 

Сдаётся мне, что дешевле будет возвращать из шаблона индекс массива, соответствующий результату поиска, а в основном коде по нему уже дёргать соотв. поле структуры.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

sadman41 пишет:

ЕвгенийП пишет:

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

Прямо вот так, чтобы всегда в обе стороны мапить - не всегда надо, но строку в enum хотелось бы. Например при разборе URL имя ресурса строковое получаем, но хранить строку в памяти - тупо. Еnum удобней. При генерации ответной страницы неплохо с той же структуры дёрнуть что-то связанное с текущим URI, таскаемым по рантайму enum-ом. Или хранить соответствие "состояние сервиса - описание - html class ". И в PROGMEM уложить этот "справочник" Такая задумка, вобщем.

пиши ужо, как есть: "колхозю жсон парсер, хачу, как у людей - вжжжих! - и строка распарсена в словари, и доступ по ключу к любому полю". ;)))))

=======================

Я не лезу, как можно заметить, почти никогда в подобные ветки - про создание "универсального чуда" для МК.

Тут есть основная причина: есть две противоречищие стратегии в программировании: удобство кодера и эффективность кода. В МК - мало ресурсов и удобство кодера, чисто ИМХО, никого не интересует. Если позволят ресурсы, то, вАААААще нет никакого спора - не только можно, но и нужно писать на NodeJS или MicroPython, которые УЖЕ реализованы для подходящих по ресурсам МК. На любом из упомянутых языков, то же жсон парсер выглядит РОВНО ОДНОЙ командой. Потому, что типонезависимый словарь/map/ассоциативный массив (это почти синонимы) - входит в базовые типы языка (JS - так просто на нем основан). То есть в этих языках УЖЕ есть то, что ты пишешь. В С++ для большой машины это есть в STL, не так удобно, как в JS или Python, но есть.

Не уверен, что для AVR, с ее ресурсами это нужно и полезно, а для ARM - "всё уже украдено до нас!" ;))) Как-то так.

Прости, я не хочу исполнять "танец птицы обломинго". Просто ты же мужик умный и не обидишься. ;)) Если ты чисто ради "скилл апгрейда" это делаешь, как баловство - то и вопросов нет.

sadman41
Offline
Зарегистрирован: 19.10.2016

Нет, жсон парсер не пишу. Делаю ровно то, что озвучил: описание статуса сервиса/тцп сокета выкидываю в html-поток, подсвечивая ошибочные состояния красным, не очень опасные - оранжевым, хорошие - зелёным. В принципе, трансформацию делаю, нагородив под этот тип функции "enum => description", "enum => htmlClass". Базу данных на МК не устраиваю, держу в прогмеме несколько массивов по 5-7 записей из 2-3  полей. 

sadman41
Offline
Зарегистрирован: 19.10.2016

Так, ну вот... Работая по методе DetSemen-а, накропал простецкое (в классы с итераторами не рискнул соваться):

templates_test.ino

#pragma GCC optimize ("O0")
#include "templates.h"

typedef enum { diMonday, diTuesday, diWednesday, diThursday, diFriday, diSaturday, diSunday } dayId_t;
typedef enum { ciViolet, ciIndigo, ciBlue, ciGreen, ciYellow, ciOrange, ciRed } colorId_t;

const char name_DAY_MONDAY[]               = "Monday";
const char name_DAY_TUESDAY[]              = "Tuesday";
const char name_DAY_WEDNESDAY[]            = "Wednesday";
const char name_DAY_THURSDAY[]             = "Thursday";
const char name_DAY_FRIDAY[]               = "Friday";
const char name_DAY_SATURDAY[]             = "Saturday";
const char name_DAY_SUNDAY[]               = "Sunday";

const char name_COLOR_VIOLET[]             = "Violet";
const char name_COLOR_INDIGO[]             = "Indigo";
const char name_COLOR_BLUE[]               = "Blue";
const char name_COLOR_GREEN[]              = "Green";
const char name_COLOR_YELLOW[]             = "Yellow";
const char name_COLOR_ORANGE[]             = "Orange";
const char name_COLOR_RED[]                = "Red";

typedef struct {
  uint8_t idx;
  const char* name;
  dayId_t id;
} dayOfWeek_t;

typedef struct {
  uint8_t idx;
  const char* name;
  colorId_t id;
} colorOfRainbow_t;

dayOfWeek_t daysOfWeek[]           = {
  { 1, name_DAY_MONDAY,    diMonday},
  { 2, name_DAY_TUESDAY,   diTuesday},
  { 3, name_DAY_WEDNESDAY, diWednesday},
  { 4, name_DAY_THURSDAY,  diThursday},
  { 5, name_DAY_FRIDAY,    diFriday},
  { 6, name_DAY_SATURDAY,  diSaturday},
  { 7, name_DAY_SUNDAY,    diSunday}
};

colorOfRainbow_t colorsOfRainbow[] = {
  { 1, name_COLOR_VIOLET, ciViolet},
  { 2, name_COLOR_INDIGO, ciIndigo},
  { 3, name_COLOR_BLUE,   ciBlue},
  { 4, name_COLOR_GREEN,  ciGreen},
  { 5, name_COLOR_YELLOW, ciYellow},
  { 6, name_COLOR_ORANGE, ciOrange},
  { 7, name_COLOR_RED,    ciRed}
};

void setup() {
  Serial.begin(115200);
  dayId_t someDayId = diFriday;
  colorId_t someColorId = ciBlue;
  char someDay[] = "Monday";
  char someColor[] = "Indigo";

  // 'enumed' ID to char*
  Serial.print("Need to reach: "); Serial.println(getNameById(someDayId, daysOfWeek, arraySize(daysOfWeek)));
  Serial.print("Need to wear: "); Serial.println(getNameById(someColorId, colorsOfRainbow, arraySize(colorsOfRainbow)));

  // char* to 'enumed' ID (and back again to char*)
  someDayId = getIdByName<dayId_t, dayOfWeek_t>(someDay, daysOfWeek, arraySize(daysOfWeek));
  Serial.print(someDay); Serial.print(" is "); Serial.println(getNameById(someDayId, daysOfWeek, arraySize(daysOfWeek)));
  someColorId = getIdByName<colorId_t, colorOfRainbow_t>(someColor, colorsOfRainbow, arraySize(colorsOfRainbow));
  Serial.print(someColor); Serial.print(" is "); Serial.println(getNameById(someColorId, colorsOfRainbow, arraySize(colorsOfRainbow)));
}

void loop() { }

templates.h

#define arraySize(_array) ( sizeof(_array) / sizeof(*(_array)) )

//***********************************************************************
template <typename ID_TYPE, typename ITEM_TYPE>
ID_TYPE getIdByName(char * _str, const ITEM_TYPE * _refArray, size_t _refArraySize) {
  ID_TYPE itemId = NULL;
  for (size_t i = 0x00; i < _refArraySize; i++) {
    //Serial.print("["); Serial.print(i); Serial.print("]"); Serial.println(_refArray[i].name);
    // strcmp_P() will be here later
    if (!strcmp(_str, _refArray[i].name)) {
      itemId = _refArray[i].id;
      break;
    }
  }
  return itemId;
}

//***********************************************************************
template < typename ID_TYPE, typename ITEM_TYPE>
const char* getNameById(const ID_TYPE _itemId, const ITEM_TYPE * _refArray, size_t _refArraySize) {
  const char* ptrItemName = NULL;
  ID_TYPE currentItemId;
  for (size_t i = 0x00; i < _refArraySize; i++) {
    // Serial.print("["); Serial.print(i); Serial.print("]"); Serial.println(_refArray[i].name);
    // memcpy_P() will be here later
    memcpy(&currentItemId, &(_refArray[i].id), sizeof(ID_TYPE));
    if (_itemId == currentItemId) {
      ptrItemName = _refArray[i].name;
      break;
    }
  }
  return ptrItemName;
}

Вроде теперь не должно быть излишнего размножения шаблонных функций? 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Очень прошу! Если тебя так прет от идей ООП, почитай про "выведение типов". При правильном применении темплейтов, параметры в уловых скобках не нужно указывать. В твоем случае именно так -- оба параметра компилятор сам высичлит из типа слева и типа параметра.

Попробуй! Должно понравится! ;))))

sadman41
Offline
Зарегистрирован: 19.10.2016

Дак я не от хорошей жизни до конкретизации шаблона докатился. Компилятор отказывался дедуктировать и прикидывался ветошью. 

И от идей меня не прёт вообще, на данном этапе развития так проще достигается цель.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Смотри: если убрать тип только из возвращаемого и внести под скобки, то выведение работает. По мне - так достаточно красиво.

я, конечно, проверил.

--------------------------------

Каюсь - по возвращаемым - не умеем. я с чем-то перепутал, вероятно, может с жавой? ;))

#pragma GCC optimize ("O0")
#include "templates.h"

//typedef enum { diMonday, diTuesday, diWednesday, diThursday, diFriday, diSaturday, diSunday } dayId_t;
//typedef enum { ciViolet, ciIndigo, ciBlue, ciGreen, ciYellow, ciOrange, ciRed } colorId_t;

const char name_DAY_MONDAY[]               = "Monday";
const char name_DAY_TUESDAY[]              = "Tuesday";
const char name_DAY_WEDNESDAY[]            = "Wednesday";
const char name_DAY_THURSDAY[]             = "Thursday";
const char name_DAY_FRIDAY[]               = "Friday";
const char name_DAY_SATURDAY[]             = "Saturday";
const char name_DAY_SUNDAY[]               = "Sunday";

const char name_COLOR_VIOLET[]             = "Violet";
const char name_COLOR_INDIGO[]             = "Indigo";
const char name_COLOR_BLUE[]               = "Blue";
const char name_COLOR_GREEN[]              = "Green";
const char name_COLOR_YELLOW[]             = "Yellow";
const char name_COLOR_ORANGE[]             = "Orange";
const char name_COLOR_RED[]                = "Red";

typedef struct {
  uint8_t idx;
  const char* name;
  dayId_t id;
} dayOfWeek_t;

typedef struct {
  uint8_t idx;
  const char* name;
  colorId_t id;
} colorOfRainbow_t;

dayOfWeek_t daysOfWeek[]           = {
  { 1, name_DAY_MONDAY,    diMonday},
  { 2, name_DAY_TUESDAY,   diTuesday},
  { 3, name_DAY_WEDNESDAY, diWednesday},
  { 4, name_DAY_THURSDAY,  diThursday},
  { 5, name_DAY_FRIDAY,    diFriday},
  { 6, name_DAY_SATURDAY,  diSaturday},
  { 7, name_DAY_SUNDAY,    diSunday}
};

colorOfRainbow_t colorsOfRainbow[] = {
  { 1, name_COLOR_VIOLET, ciViolet},
  { 2, name_COLOR_INDIGO, ciIndigo},
  { 3, name_COLOR_BLUE,   ciBlue},
  { 4, name_COLOR_GREEN,  ciGreen},
  { 5, name_COLOR_YELLOW, ciYellow},
  { 6, name_COLOR_ORANGE, ciOrange},
  { 7, name_COLOR_RED,    ciRed}
};

void setup() {
  Serial.begin(115200);
  dayId_t someDayId = diFriday;
  colorId_t someColorId = ciBlue;
  char someDay[] = "Monday";
  char someColor[] = "Indigo";

  // 'enumed' ID to char*
  Serial.print("Need to reach: "); Serial.println(getNameById(someDayId, daysOfWeek, arraySize(daysOfWeek)));
  Serial.print("Need to wear: "); Serial.println(getNameById(someColorId, colorsOfRainbow, arraySize(colorsOfRainbow)));

  // char* to 'enumed' ID (and back again to char*)
  getIdByName(someDay,someDayId, daysOfWeek, arraySize(daysOfWeek));
  Serial.print(someDay); Serial.print(" is "); Serial.println(getNameById(someDayId, daysOfWeek, arraySize(daysOfWeek)));
  getIdByName(someColor, someColorId, colorsOfRainbow, arraySize(colorsOfRainbow));
  Serial.print(someColor); Serial.print(" is "); Serial.println(getNameById(someColorId, colorsOfRainbow, arraySize(colorsOfRainbow)));
}

void loop() { }

и темплейты:

#define arraySize(_array) ( sizeof(_array) / sizeof(*(_array)) )

enum dayId_t{ diMonday, diTuesday, diWednesday, diThursday, diFriday, diSaturday, diSunday } ;
enum colorId_t{ ciViolet, ciIndigo, ciBlue, ciGreen, ciYellow, ciOrange, ciRed } ;

//***********************************************************************
template <typename ID_TYPE, typename ITEM_TYPE>
void getIdByName(char * _str, ID_TYPE& id, ITEM_TYPE * _refArray, size_t _refArraySize) {
  ID_TYPE itemId = NULL;
  for (size_t i = 0x00; i < _refArraySize; i++) {
    //Serial.print("["); Serial.print(i); Serial.print("]"); Serial.println(_refArray[i].name);
    // strcmp_P() will be here later
    if (!strcmp(_str, _refArray[i].name)) {
      itemId = _refArray[i].id;
      break;
    }
  }
  id = itemId;
}

//***********************************************************************
template < typename ID_TYPE, typename ITEM_TYPE>
const char * getNameById(const ID_TYPE _itemId, ITEM_TYPE * _refArray, size_t _refArraySize) {
  const char* ptrItemName = NULL;
  ID_TYPE currentItemId;
  for (size_t i = 0x00; i < _refArraySize; i++) {
    // Serial.print("["); Serial.print(i); Serial.print("]"); Serial.println(_refArray[i].name);
    // memcpy_P() will be here later
    memcpy(&currentItemId, &(_refArray[i].id), sizeof(ID_TYPE));
    if (_itemId == currentItemId) {
      ptrItemName = _refArray[i].name;
      break;
    }
  }
  return ptrItemName;
}

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Да, проверил себя, в джаве тоже так нельзя.

Ну тогда можно, как я много где видел, сделать return не void а продублировать ID. Но аргумент ID с нужным типом под скобки включить придется, по мне - так это красивее, чем в угловых скобках что-то писать, но тут - "на вкус и цвет...." и что-то там про фломастеры.

sadman41
Offline
Зарегистрирован: 19.10.2016

Дальнейшее развитие идеи и её практическое применение показало, что удобней сделать так:

typedef enum { diUnknownDay, diMonday, diTuesday, diWednesday, diThursday, diFriday, diSaturday, diSunday } 

template <typename ID_TYPE, typename ITEM_TYPE>
ID_TYPE getIdByName(char  *_itemName, const ID_TYPE& _defaultId, ITEM_TYPE * _refArray, size_t _refArraySize) {
  ID_TYPE itemId = _defaultId;
  for (size_t i = 0x00; i < _refArraySize; i++) {
    if (!strcmp(_str, _refArray[i].name)) {
      itemId = _refArray[i].id;
      break;
    }
  }
  id = itemId;
}

С вызовом

someDayId = getIdByName(someDay, diUnknownDay, daysOfWeek, arraySize(daysOfWeek));

Две птички одним камнем: возвращаемый тип через формальный параметр заводим, избавляясь от конкретизации шаблона, и обрабатываем ситуацию с именем, в соответствие которому не поставлен ни один Id.