Интерфейсы-миксины

Николай Бахтин
Николай Бахтин

Интерфейсы-миксины вак отдельная сущность

Пускай объявление интерфейса имеет вид:
interface MyInterface1
{
   void Method1(const char* param1, std::string param2) = 0;
   void Method2(std::map<std::string,std::string>& param1, const std::string& param2) = 0;
};

interface Parent1 : MyInterface1
{
    void Method1(const char* param1, std::string param2);
    void Method2(std::map<std::string,std::string>& param1, const std::string& param2);
}parent1;

interface Parent2 : MyInterface1
{
    void Method1(const char* param1, std::string param2);
    void Method2(std::map<std::string,std::string>& param1, const std::string& param2);
}parent2;

void Parent1::Method1(const char* param1, std::string param2)
{
   //some code
}

void Parent1::Method2(std::map<std::string,std::string> param1, const std::string& param2)
{
   //some code
}

void Parent2::Method1(const char* param1, std::string param2)
{
   //some code
}

void Parent2::Method2(std::map<std::string,std::string> param1, const std::string& param2)
{
   //some code
}

Методы интерфейса вызываются так-же, как и методы класса. Только в вызове метода нет скрытого параметра "this".

Parent1 p1;
Parent2* p2 = &parent2;
p1.Method1("Hello World!!!", some_string);
p2->Method1("Hello World!!!", some_string);

Так же можно использовать using с указанием типа интерфейса и типа данных, чтобы в той области видимости, где объявлен using можно было вызывать методы интерфейса один в один как миксины;

using Parent1<char*>;
"Hello World".Method1(some_string);

std::map<std::string, std::string> params;
params.Method2("addr");

Можно сделать оптимизацию, если компилятор знает точный тип интерфейса, то не надо делать виртуальный вызов. Будет обычный вызов или inline функции.
В определенных случаях это позволит "zero cost" абстракции.

Так же интерфейс можно сделать из уже существующийх функций.

interface CStringUtils
{
   size_t length(const char* str) = &strlen;
   double to_double(const char* str) = &atof;
   int to_int(const char* str) = &atoi;
}

using CStringUtils<const char*>;

size_t v1 = "Hello world".length();

const char* v2_str = "77.5";
double v2 = v2_str.to_double()

const char* v3_str = "1234";
int v3 = v3_str.to_int();

Несмотря на то, что код в ООП стиле, компилятор может заменить вызовы методов миксинов на прямой вызов соотвестующийх функций из string.h

-1
рейтинг
5 комментариев
Игорь Гусаров

Здравствуйте, Николай!

1. В чём видится основная польза от миксинов? Т.е. какую проблему (или неудобство) они позволяют решить? Дадут ли они только новый синтаксис вызова, или предполагается как-то их использовать ("подмешивать") в определения классов?

2. Вероятно, в примерах опечатка и интерфейсы должны были быть объявлены как шаблоны? Иначе не совсем понятно, на что влияет параметр в угловых скобках, например CStringUtils<const char*>.

3. Если основной целью является возможность вызвать strlen(c_string) в виде c_string.strlen(), то это уже пытались ввести в язык. Пока безуспешно. У предложения нашлись как ярые сторонники, так и ярые противники. Чтобы не пересказывать здесь всю историю, взгляните на документы P0131R0 (обсуждение возникающих проблем), N4474 (первоначальное предложение) и P0251R0 (более узкое предложение, которое всё равно не прошло).

Игорь Гусаров
Николай Бахтин

>>1. В чём видится основная польза от миксинов?

Хороший вопрос требует хорошего ответа. Надо будет над этим подумать.

 

>>2. Вероятно, в примерах опечатка и интерфейсы должны были быть объявлены как шаблоны? Иначе не совсем понятно, на что влияет параметр в угловых скобках, например CStringUtils<const char*>.

Моя мысль была в том, что "подмешивание" методов миксинов в структуры и классы происходит только тогда, когда мы указываем это явно. Конструкция вида:

using имя-интерфейса<имя-типа>;

говорит о том, что в этой области видимости мы для указанного типа подмешиваем методы указанного интерфейса. Если такого указания не делать, то никакого "подмешивания" не происходит. Пример:

void function1(const char* name)
{
   using CStringUtils<const char*>;
   size_t name_len = name.strlen();  //Ok

}

int main()
{
   const char* name = "Tomas";
  
function1(name);
   size_t name_len = name.strlen(); //Error

   return 0;
}

 

Возможно данный вариант синтаксиса не самый лучший. Можно было бы как-то подругому, например:

using CStringUtils(const char*);
using CStringUtils : const char*;
using CStringUtils[const char*];
using CStringUtils for const char*;

>>3. ... взгляните на документы P0131R0 (обсуждение возникающих проблем), N4474 (первоначальное предложение) и P0251R0 (более узкое предложение, которое всё равно не прошло)...

Спасибо за информацию

Николай Бахтин
Обновлено 
Игорь Гусаров

А, то есть вот в этом примере должно было быть две директивы using?

using Parent1<const char*>;
"Hello World".Method1(some_string);

using Parent1<std::map<std::string, std::string>>;
std::map<std::string, std::string> params;
params.Method2("addr");

Игорь Гусаров
Николай Бахтин

Да, все верно. Это я ошибся .

Николай Бахтин
Обновлено 
Nate Reinar Windwood

Не нужна отдельная сущность, лучше допилить концепты до полноценных тайпклассов.

Nate Reinar Windwood
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).
Все предложения