LINUX.ORG.RU

Функция-шаблон С++ в статической библиотеке?

 ,


0

3

Пишу под Debian, набросал библиотечку с набором функций. По ходу дела понадобилось в некоторых функциях работать с различными типами данных, описал как шаблоны. Теперь undefined reference при сборке проекта, который эту библиотеку подключает и вызывает функции-шаблоны. Можно ли это обойти? Или шаблоны только в рамках одного проекта могут быть используемы?


Ответ на: комментарий от azelipupenko

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

Deleted
()
Ответ на: комментарий от Deleted

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

Но тут же речь о шаблонах цепепе. Которые, в сравнении с реальными макрами, ограничены и убоги.

azelipupenko
()
Ответ на: комментарий от azelipupenko

Теперь это определение

Теперь оно исходит от тебя, и поэтому значит ровным счётом ничего. :-D

Общепризнанного определения ты найти не смог. Поэтому моё определение не хуже остальных.

Ага, стоило тебя попросили нарисовать реализовать аналог стандартного assert() на шаблонах

Инструменты нужно применять по назначению. В Си++ для реализации assert не обязательно использовать шаблоны. if(0), и компилятор выбросит код, даже если этот 0 был const int.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Теперь оно исходит от тебя, и поэтому значит ровным счётом ничего. :-D

Нет, оно исходит из книги Страуструпа.

Общепризнанного определения ты найти не смог.

Я нашёл их несколько и показал.

Поэтому моё определение не хуже остальных.

Твоё определение ничем не подкреплено. Ты не смог доказать, что шаблоны - это макросы с гигиеной. Даже не смог реализовать assert() на шаблонах. Поэтому как раз твоё определение не значит ничего. Как я и сказал - это твоя личная фантазия.

Инструменты нужно применять по назначению. В Си++ для реализации assert не обязательно использовать шаблоны. if(0), и компилятор выбросит код, даже если этот 0 был const int.

Инструменты надо называть своими именами. Шаблоны в цепепе - всего лишь средство для подстановки типов или интегралов в код классов или функций, а не какие не макросы.

azelipupenko
()
Ответ на: комментарий от azelipupenko

Нет, оно исходит из книги Страуструпа.

Нельзя менять смысл цитат и продолжать их приписывать авторам. Иначе я мог бы делать так:

Нет, оно не исходит из книги Страуструпа.

Вот. Это исходит от тебя.

i-rinat ★★★★★
()
Ответ на: комментарий от azelipupenko

А, ну да, я не вчитывался в твои простыни. Ты же там даже не сделал из фразы определение. :-D

Вопрос снимается. Ты даже не пытался.

i-rinat ★★★★★
()
Ответ на: комментарий от tailgunner

Это не гарантируется. Там вообще много чего не гарантируется.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

А, ну да, я не вчитывался в твои простыни. Ты же там даже не сделал из фразы определение. :-D

Так научись читать.

Вопрос снимается. Ты даже не пытался.

Пожалуй да, ты безнадёжен.

azelipupenko
()
Ответ на: комментарий от Miguel

Вот-вот. И то, что там называется «дженерики» — это те же макросы. Настоящих дженериков там нет и, похоже, не будет.

А какие дженерики настоящие, где на них можно посмотреть или почитать про них?

Virtuos86 ★★★★★
()
Ответ на: комментарий от azelipupenko

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

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

А про определение «настоящих» дженериков не хочешь ответить? Я в дедукции не силён, чтобы самому искать, чем тебя не устроили Rust'овые дженерики.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Miguel

Параметрический полиморфизм там есть.

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

Забавно, что ты сам признаешь - он есть и работает.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

А в чём тогда прикол от дженериков? Мономорфизация дает статический диспатчинг => производительность (ценой мультиплицирования кода). По ссылке такая портянка, что я пал духом, еще не приступив к чтению :( Ладно, хаскеллисты странные пуритане.

Virtuos86 ★★★★★
()
Ответ на: комментарий от azelipupenko

Судя по перечню — как минимум с жирненьким и/или заоверинжиниренным рантаймом.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

По ссылке такая портянка, что я пал духом, еще не приступив к чтению :( Ладно, хаскеллисты странные пуритане.

По ссылке высказывается мнение, что насчет Хаскеля он тоже неправ, так что с настоящими хаскелистами всё норм.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Там у него более подробный список претензий, которые нужно проверять, чтобы дать им оценку, чем здесь. Хотя вот:

Дженерик (в жабе, скале, окамле, хаскеле и прочих нормальных языках) — это одна функция, которая готова работать с разными типами. Она может быть скомпилирована отдельно, и после этого принимать ЛЮБОЙ тип, который соответствует заданным — при её создании — требованиям. То есть, компилятор, конечно, вправе сделать особые, оптимизированные её версии для разных типов (и, скажем, GHC так и делает), но для программиста это всё прозрачно: одна функция.

выглядит странно, потому что с его же слов, GHC делает то же, самое, что и rustc, но при этом в хаскелле настоящие дженерики, а в расте — нет.

А самое важное — семантика этой функции определена в терминах производимых ею действий.

А смысл этого уточнения теряется в заоблачных высотах мира ФП, надо полагать.

Virtuos86 ★★★★★
()
Ответ на: комментарий от azelipupenko

я конечно не гуру спп и может я не сильно понял что ты имеешь ввиду, но допустим:

#include <cstdio>

template <bool B>
void my_assert();

template <>
void my_assert <true> () {puts("exit -1");}

template <>
void my_assert <false> () {puts("ignore");}

int main()
{
  constexpr bool b = true;
  my_assert <b> ();
}

И да, приводить определение макросов из лиспа в тред по спп это уже диагноз не?

anonymous
()
Ответ на: комментарий от anonymous

И да, приводить определение макросов из лиспа в тред по спп это уже диагноз не?

А давать определение ООП из смоллтока тоже будет диагнозом, или всё-таки ссылкой на каноничный источник?

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

Там у него более подробный список претензий

Там более раскидистое изложение одной претензии, насколько я понял. Забавно, что дженерики в Swift его устроили бы.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от anonymous

я конечно не гуру спп и может я не сильно понял что ты имеешь ввиду, но допустим:

Это не замена assert(). assert() выводит в stderr имя файла и номер строки неверного утверждения, в этом и состоит его удобство. Твои реализации выводят просто строки.

azelipupenko
()
Ответ на: комментарий от anonymous

И да, приводить определение макросов из лиспа в тред по спп это уже диагноз не?

Лол. А притягивать в тред по цепепе понятие гигиенических макросов, которое изначально и появились из-за проблем анафорических макросов в Лиспе, и утверждать, что шаблоны в цепепе - это как раз и есть гигиенические макросы - это диагноз или нет?

azelipupenko
()
Ответ на: комментарий от Virtuos86

Про определение — не хочу. Определения сосут.

Про «чем не устроили» — ну, попробуй воспроизвести на C++ то, что C#/Java/Haskell/Scala делают без проблем:

using System;
interface ScalarProduct<A> {
  int scalarProduct(A second);
}
class Nil : ScalarProduct<Nil> {
  public Nil(){}
  public int scalarProduct(Nil second) {
    return 0;
  }
}
class Cons<A> : ScalarProduct<Cons<A>> where A : ScalarProduct<A> {
  public int value;
  public A tail;
  public Cons(int _value, A _tail) {
    value = _value;
    tail = _tail;
  }
  public int scalarProduct(Cons<A> second){
    return value * second.value + tail.scalarProduct(second.tail);
  }
}
class _Test{
  public static int main(int n){
    return _main(n, 0, new Nil(), new Nil());
  }
  public static int _main<A>(int n, int i, A first, A second) where A : ScalarProduct<A> {
    if (n == 0) {
      return first.scalarProduct(second);
    } else {
      return _main(n-1, i+1, new Cons<A>(2*i+1,first), new Cons<A>(i*i, second)); // Works
      //return _main(n-1, i+1, first, new Cons<A>(i*i, second)); // Doesn't work
    }
  }
}
public class Test{
  public static void Main(){
    Console.Write("Enter a number: ");
    int val = Convert.ToInt32(Console.ReadLine());
    Console.WriteLine(_Test.main(val));
  }
}

Это, конечно, высосанная из пальца задачка, но фишка в том, что невозможность сделать подобное в плюсах напрямую вытекает из того, что шаблоны — это инструмент генерации кода, а не дженерики. Отсюда и невозможность один раз откомпилировать шаблоны, а потом только использовать (Rust притворяется, что компилирует егойные «дженерики», но на самом деле просто запихивает исходный код в объектник. Ну ладно, не исходный код, а AST, но это почти одно и то же). Отсюда и километровые сообщения об ошибках, если ты используешь шаблон не так, как предполагалось. И так далее.

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

Miguel ★★★★★
()
Последнее исправление: Miguel (всего исправлений: 1)
Ответ на: комментарий от tailgunner

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

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

Ещё раз, для особо одарённых

От особо одаренных, как я вижу.

Есть подделка, которая в большинстве случаев работает

Еще немного, и начнется дискуссия о философском зомби.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от Virtuos86

GHC делает то же, самое, что и rustc

Нет. GHC делает оптимизацию, не влияющую на поведение.

Ну, грубо: то, что в GHC — это как если компилятор предвычисляет константные выражения, заменяя timeout=60*60*1000 на timeout=3600000. А то, что в Rust — это как если бы компилятор вообще не умел компилить арифметику, кроме как с константами.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

То есть, подытоживая, GHC оптимизации ради генерирует специализированные версии обобщенной полиморфной функции (т.е. выполняет мономорфизацию) для каждого отличающегося случая применения оной, но в общем случае может держать в памяти только одну копию дженерика, а rustc выполняет упомянутую оптимизацию всегда, и ты считаешь это одним из важных отличий труЪ дженериков от компромиссных решений?

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

а rustc выполняет упомянутую оптимизацию всегда

...но НЕ МОЖЕТ иметь дженерик, работающий для всех случаев. И посему может применять дженерик только к ИЗВЕСТНОМУ ПРИ КОМПИЛЯЦИИ конечному набору типов.

Ещё раз: это примерно как если бы Rust мог компилировать арифметику ТОЛЬКО с константными аргументами. Конечно, вся арифметика была бы супер-оптимизированной, только нафиг никому не нужной.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

Что ж, любопытная информация. Надо будет поэкспериментировать. Спасибо за потраченное время. Я вопросов больше не имею.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Miguel

А можно вот этот при на нормальном языке переписать, например Haskell, а то ООП языки ведь не читаемы от слова совсем.

Я так понял у вас тут односвязный список, только вот почему аналог этого кода не должен завестись в каком нибудь Rust'е я не понял.

Nexmean
()
Ответ на: комментарий от Nexmean

Без базара.

class ScalarProduct a where scalarProduct :: a -> a -> Integer

data Nil = Nil

data Cons a = Cons Integer a

instance ScalarProduct Nil where scalarProduct Nil Nil = 0

instance ScalarProduct a => ScalarProduct (Cons a) where 
   scalarProduct (Cons i1 a1) (Cons i2 a2) = i1 * i2 + a1 * a2

main'' :: ScalarProduct a => Integer -> Integer -> a -> a -> Integer
main'' 0 _ first second = scalarProduct first second
main'' n i first second = main' (n-1) (i+1) (Cons (2*i+1) first) (Cons (i*i) second)
-- Won't work:
-- main'' n i first second = main' (n-1) (i+1) first (Cons (i*i) second)

main' :: Integer -> Integer
main' n = main'' n 0 Nil Nil

main :: IO ()
main = do
    putStr "Enter a number: "
    n <- readLn
    putStrLn $ main' n

Не сработает потому, что и в C++, и в Rust дженерик должен применяться к конечному набору типов, известному на момент компиляции программы. Здесь количество типов конечно, но определяется в процессе работы программы.

Miguel ★★★★★
()
Ответ на: комментарий от anonymous

http://web.archive.org/web/20120412092416/https://migmit.livejournal.com/3268...

Вы написали какой-то код, который якобы, как подсказывает вам ваше образное мышление на основании туманных эмоциональных образов, использует параметрический полиморфизм, а затем переписали этот код на другой язык, где он не заработал. И на этом основании заявляете, что в С++ нету параметрического полиморфизма, а в Java он есть.

Смешно.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

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

Nexmean
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.