Недавно я использовал следующее в приложении, которое хорошо работает для моих нужд.
.htaccess
<IfModule mod_rewrite.c>
# enable rewrite engine
RewriteEngine On
# if requested url does not exist pass it as path info to index.php
RewriteRule ^$ index.php?/ [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) index.php?/$1 [QSA,L]
</IfModule>
index.php
foreach (explode ("/", $_SERVER['REQUEST_URI']) as $part)
{
// Figure out what you want to do with the URL parts.
}
Вам не нужно создавать какие-либо объекты, создающие экземпляры MyTemplate1<T>
в модуле компиляции, чтобы видеть объекты типа info, описывающие классы реализации этого шаблона, в глобальной таблице символов объектного файла. Вам нужно только сослаться на typeid
такого класса: -
$ cat main.cpp
#include <typeinfo>
template<class T>
class MyTemplate1
{
public:
T a;
MyTemplate1(T other){
a = other;
}
};
int main(void)
{
return (typeid(MyTemplate1<int>) == typeid(MyTemplate1<float>));
}
$ clang++ -Wall -c main.cpp
$ readelf -s -W main.o | grep MyTemplate1
5: 0000000000000000 16 OBJECT WEAK DEFAULT 15 _ZTI11MyTemplate1IfE
6: 0000000000000000 16 OBJECT WEAK DEFAULT 10 _ZTI11MyTemplate1IiE
7: 0000000000000000 17 OBJECT WEAK DEFAULT 13 _ZTS11MyTemplate1IfE
8: 0000000000000000 17 OBJECT WEAK DEFAULT 8 _ZTS11MyTemplate1IiE
$ c++filt _ZTI11MyTemplate1IfE
typeinfo for MyTemplate1<float>
$ c++filt _ZTI11MyTemplate1IiE
typeinfo for MyTemplate1<int>
$ c++filt _ZTS11MyTemplate1IfE
typeinfo name for MyTemplate1<float>
$ c++filt _ZTS11MyTemplate1IiE
typeinfo name for MyTemplate1<int>
Эти typeinfo
объекты существуют, потому что, как прокомментировал @Peter, стандарт C ++ требует, чтобы typeid
ссылается на объект статической длительности хранения
Что именно происходит под капотом?
blockquote>Вы можете задаться вопросом: почему компилятор делает это
typeinfo
] символы объекта слабые , а не просто глобальные? Почему он определяет их в разных разделах объектного файла? (разделы 10 и 15 моего объектного файла, разделы 2894 и 2899 вашего).И если мы проверим, что еще находится в этих разделах:
$ readelf -s main.o | egrep '(10 |15 )' 5: 0000000000000000 16 OBJECT WEAK DEFAULT 15 _ZTI11MyTemplate1IfE 6: 0000000000000000 16 OBJECT WEAK DEFAULT 10 _ZTI11MyTemplate1IiE
, мы видим, что каждый объект является единственным в своем разделе. Почему так?
В моих
main.o
эти разделы 10 и 15:$ readelf -t main.o | egrep '(\[10\]|\[15\])' [10] .rodata._ZTI11MyTemplate1IiE [15] .rodata._ZTI11MyTemplate1IfE
Каждый из них - только для чтения раздел данных в смысл:
__attribute__((section(.rodata._ZTI11MyTemplate1IiE))) __attribute__((section(.rodata._ZTI11MyTemplate1IfE)))
, который не содержит ничего, кроме определения объекта, после которого он назван.
Компилятор предоставляет каждому из объектов раздел данных для всех себе по той же причине, по которой он создает символы
WEAK
. Ссылки наtypeid(MyTemplate1<X>)
для произвольного типаX
могут быть сделаны в нескольких единицах перевода в пределах одной и той же связи, что#include
определениеMyTemplate1
. Чтобы предотвратить сбой связи в таких случаях с ошибкой множественного определения , компилятор делает символы слабыми. Линкер будет допускать множественные определения слабого символа , разрешая все ссылки просто на первое определение, которое представляет себя, и игнорируя остальные. Посредством выделения уникального раздела данных (или функционального раздела, в зависимости от случая) определению каждого слабого символа, создающего экземпляр шаблона, компилятор дает компоновщику свободу отбрасывать любые избыточные секции данных или функций, которые определяют такой же слабый символ без риска побочного ущерба для программы. См.$ cat MyTemplate1.hpp #pragma once template<class T> class MyTemplate1 { public: T a; MyTemplate1(T other){ a = other; } }; $ cat foo.cpp #include "MyTemplate1.hpp" #include <typeinfo> int foo() { return typeid(MyTemplate1<int>) == typeid(MyTemplate1<float>); } $ cat bar.cpp #include "MyTemplate1.hpp" #include <typeinfo> int bar() { return typeid(MyTemplate1<int>) != typeid(MyTemplate1<float>); } $ cat prog.cpp extern int foo(); extern int bar(); int main() { return foo() && bar(); }
Если мы скомпилируем:
$ clang++ -Wall -c prog.cpp foo.cpp bar.cpp
и ссылку (с некоторой диагностикой) следующим образом:
$ clang++ -o prog prog.o bar.o foo.o \ -Wl,-trace-symbol=_ZTI11MyTemplate1IfE \ -Wl,-trace-symbol=_ZTI11MyTemplate1IiE \ -Wl,-Map=mapfile /usr/bin/ld: bar.o: definition of _ZTI11MyTemplate1IfE /usr/bin/ld: bar.o: definition of _ZTI11MyTemplate1IiE /usr/bin/ld: foo.o: reference to _ZTI11MyTemplate1IfE /usr/bin/ld: foo.o: reference to _ZTI11MyTemplate1IiE
ввод
bar.o
доfoo.o
], то компоновщик выбирает определения из_ZTI11MyTemplate1I(f|i)E
изbar.o
и игнорирует определения изfoo.o
, разрешая ссылки вfoo.o
на определения изbar.o
. И файл карты показывает:mapfile (1)
... Discarded input sections ... .rodata._ZTI11MyTemplate1IiE 0x0000000000000000 0x10 foo.o ... .rodata._ZTI11MyTemplate1IfE 0x0000000000000000 0x10 foo.o ...
, что определения в
foo.o
были отброшены. Если мы свяжемся с порядкомbar.o
иfoo.o
в обратном порядке:$ clang++ -o prog prog.o foo.o bar.o \ -Wl,-trace-symbol=_ZTI11MyTemplate1IfE \ -Wl,-trace-symbol=_ZTI11MyTemplate1IiE \ -Wl,-Map=mapfile /usr/bin/ld: foo.o: definition of _ZTI11MyTemplate1IfE /usr/bin/ld: foo.o: definition of _ZTI11MyTemplate1IiE /usr/bin/ld: bar.o: reference to _ZTI11MyTemplate1IfE /usr/bin/ld: bar.o: reference to _ZTI11MyTemplate1IiE
, то мы получим противоположные результаты. Определения из
foo.o
связаны между собой:mapfile (2)
... Discarded input sections ... .rodata._ZTI11MyTemplate1IiE 0x0000000000000000 0x10 bar.o ... .rodata._ZTI11MyTemplate1IfE 0x0000000000000000 0x10 bar.o ...
из
bar.o
выброшены. Этот принцип компоновки «первым пришел - первым обслужен» хорош, потому что - и только , потому что - определениеtemplate<class T> MyTemplate1
, которое компилятор нашел в блоке переводаfoo.cpp
, было идентично ] к тому, которое было найдено вbar.cpp
, условие, которое стандарт C ++ требует , в правило единого определения , но которое компилятор C ++ ничего не может сделать для принудительного применения .Вы можете сделать, в основном, те же самые наблюдения относительно символов создания шаблона в целом, и то, что вы видите с помощью clang ++, по сути то же самое, что вы увидите с g ++.