Приветствую всех!Данная тема относиться к C++ но немножко будет нестадартна тем, что для ее расмотрения нужна звуковая система OSS ( http://developer.opensound.com )
Ближе к делу.OSS - когда-то проприетарные, сейчас Open Source GPL v.2 звуковые драйвера для всех видов UNIX.
Избрал именно их - ибо единственные в своем роде которые пашут на разных UNIX.Нужно заполнить контейнер неякими данными.
Проблема в том, что если использовать
std::map<std::string, int> список создаеться нормально.
А вот при std::map<const char*, int> не хочет.Мне нужно как раз char* и не std::string...
Пробывал заполнять другими данными - без проблем с char* и с std::string.
Какой-то полтергейст. Не понятно почему не хочет именно в моем случае:Ниже выкладываю текст.
Компилил с использованием OSS v.4 и gcc 4.1.2Проверял в GNU/Linux, должно работать также и в *BSD.
ВНИМАНИЕ: OSS v.4! OSS < v.4 не подойдет... Там совершенно другой API. Хотя все может быть..Если нужна помощь в установке самых драйверов в GNU/Linux, FreeBSD - помогу.
/*
Using OSS v.4.0 (b071114/200711211324) GPL license. OSS API v.40003
ICH AC97 Mixer (AD1985)GCC 4.1.2 GNU/Linux
g++ bug.cpp -o bug
./bugCreating not correctly list with using std::map<const char*, int>
Only work with std::map<std::string, int>Two part of this file (working and not working) are identicaly, with once defference:
Not working: std::map<const char*, int>
Working: std::map<std::string, int>Author: Alex J. Ivasyuv // SIEGERSTEIN
Bug version under GPL, working under proprietary :)) // joke
*/#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sys/ioctl.h>
#include <fcntl.h>#include "/usr/lib/oss/include/sys/soundcard.h"
int mixer_fd = -1;
int mixer_dev = 0;
int nrext = -1;
oss_mixext ext;int main() {
if ((mixer_fd = open("/dev/mixer", O_RDWR)) == -1) {
perror ("/dev/mixer");
exit (-1);
}
nrext = mixer_dev;
if ( ioctl ( mixer_fd, SNDCTL_MIX_NREXT, &nrext ) == -1 ) { // nrext = 72
perror ("SNDCTL_MIX_NREXT");
return 1;
}
if (ioctl(mixer_fd, SNDCTL_MIX_EXTINFO, &ext) == -1) {
perror("SNDCTL_MIX_EXTINFO");
return 1;
}
/*******************************************************************************************/
/* NOT WORKING */
std::map<const char*, int> NOT_AVAIBLE_MIX_DEV;
for ( int i = 1; i < nrext; i++ ) {
ext.dev = mixer_dev;
ext.ctrl = i;
ioctl(mixer_fd, SNDCTL_MIX_EXTINFO, &ext);
if ( ( ext.type == MIXT_MONOSLIDER ) || ( ext.type == MIXT_STEREOSLIDER ) || (ext.type == MIXF_RECVOL ) ) {
if ( ext.extname != NULL ) {NOT_AVAIBLE_MIX_DEV[ext.extname] = i;
}
}
}
std::cout << std::endl << "******************************************" << std::endl;
std::cout << "Starting NOT working method..." << std::endl << std::endl;
for (std::map<const char*, int>::iterator it = NOT_AVAIBLE_MIX_DEV.begin(); it != NOT_AVAIBLE_MIX_DEV.end(); ++it) {
std::cout << "it->first: " << it->first << std::endl;
std::cout << "it->second: " << it->second << std::endl;
std::cout << std::endl;
}
std::cout << "End NOT working method." << std::endl;
std::cout << "******************************************" << std::endl << std::endl;
/****************************************************************************************/
/*******************************************************************************************/
/* WORKING */
std::map<std::string, int> NORM_AVAIBLE_MIX_DEV;
for ( int i = 1; i < nrext; i++ ) {
ext.dev = mixer_dev;
ext.ctrl = i;
ioctl(mixer_fd, SNDCTL_MIX_EXTINFO, &ext);
if ( ( ext.type == MIXT_MONOSLIDER ) || ( ext.type == MIXT_STEREOSLIDER ) || (ext.type == MIXF_RECVOL ) ) {
if ( ext.extname != NULL ) {NORM_AVAIBLE_MIX_DEV[ext.extname] = i;
}
}
}
std::cout << "******************************************" << std::endl;
std::cout << "Starting working method..." << std::endl << std::endl;
for (std::map<std::string, int>::iterator it = NORM_AVAIBLE_MIX_DEV.begin(); it != NORM_AVAIBLE_MIX_DEV.end(); ++it) {
std::cout << "it->first: " << it->first << std::endl;
std::cout << "it->second: " << it->second << std::endl;
std::cout << std::endl;
}
std::cout << "End working method." << std::endl;
std::cout << "******************************************" << std::endl << std::endl;
/****************************************************************************************/
return 0;
}В итоге выходит такое:
$ ./bug
******************************************
Starting NOT working method...it->first: vmix0-in
it->second: 34End NOT working method.
************************************************************************************
Starting working method...it->first: aux1
it->second: 20it->first: cd
it->second: 15it->first: center
it->second: 34it->first: igain
it->second: 18it->first: line
it->second: 9it->first: mic
it->second: 12it->first: mono
it->second: 26it->first: pcm
it->second: 5it->first: phone
it->second: 23it->first: rear
it->second: 32it->first: speaker
it->second: 7it->first: video
it->second: 29it->first: vol
it->second: 2End working method.
******************************************Кто-то может почему оно не работает? Может я что-то не то делаю?
Зарание спасибо за ответ.
> oss_mixext ext;<skip>
> if (ioctl(mixer_fd, SNDCTL_MIX_EXTINFO, &ext) == -1) {
> perror("SNDCTL_MIX_EXTINFO");
> return 1;
>}<skip>
/*******************************************************************************************/
> /* NOT WORKING */
> std::map<const char*, int> NOT_AVAIBLE_MIX_DEV;
>
> for ( int i = 1; i < nrext; i++
>) {<skip>
>
> NOT_AVAIBLE_MIX_DEV[ext.extname] = i;NOT_AVAIBLE_MIX_DEV обьявлен как мап с ключем "указатель на const char". ext.extname - это тот самый указатель. Насколько я понимаю, структура ext создана одна и при каждом вызове ioctl() все та же структура заполняется заново. Я не знаю точно как она заполняется внутри ioctl(), но могу предположить что значение указателя ext.extname каждый раз одно и то же - и, соответственно, значение внутри квдратных скобок в операторе присваивания
NOT_AVAIBLE_MIX_DEV[ext.extname] = i;
то же одно и тоже на каждой итерации цикла (нет, это не содержание строки, это адрес строки (32-бит. число (или 64-бит, если машина 64-бит.)) Именно в этом случае мы после окончания цикла обнаружим в мапе только один элемент: на каждой итерации элемент с этим ключом (значением указателя, которое в цикле постоянно) перезаписывается - новые элементы не добавляются.
Почему просто при замене char * на std::string все начинает работать? Потому что в этом
случае при каждом выполненииNOT_AVAIBLE_MIX_DEV[ext.extname] = i;
создается новый обьект std::string значение которого и используется как ключ мапа. Теперь мап описан как мап из std::string в int, он ожидает значения типа std::string как ключ. std::string зато имеет конструктор который принимает char *, именно он и вызывается когда нашему мапу передается char * как ключ, что бы преобразовать char * в std::string. При этом, обратите внимание, ключ - это не адрес std::string, а сам std::string - который имеет переопределенные операторы сравнения сравнивающие именно содержание строки. Что, собственно, и делает std::string хорошим ключем для мапа.
Так вот, поскольку теперь при каждой итерации цикла мапу даются разные ключи (с разным содержанием строк), каждый раз в мап добавляется новый елемент.
Спасибо elvenic за ответ. Очень выручил.
Даже в списках рассылки OSS не кто не ответил...> но могу предположить что значение указателя ext.extname каждый раз одно и то же
Действительно это верно, поменял
std::map<std::string, int> NORM_AVAIBLE_MIX_DEV
на
std::map<int, std::string> NORM_AVAIBLE_MIX_DEV
и map сразу заполнился нужным количеством значений (но к сожалению не верными значениями ).> создается новый обьект std::string значение которого и используется как ключ мапа.
В конечном итоге решил проблему вот так:
std::map<const char*, int> NOT_AVAIBLE_MIX_DEV;
std::map<std::string, int> tmpCont;
for ( int i = 1; i < nrext; i++ ) {
ext.dev = mixer_dev;
ext.ctrl = i;
ioctl(mixer_fd, SNDCTL_MIX_EXTINFO, &ext);
if ( ( ext.type == MIXT_MONOSLIDER ) || ( ext.type == MIXT_STEREOSLIDER ) || (ext.type == MIXF_RECVOL ) ) {
if ( ext.extname != NULL ) {
tmpCont[ext.extname] = i;
}
}
}
for (std::map<std::string, int>::iterator it = tmpCont.begin(); it != tmpCont.end(); ++it) {
const char * tmpVal = it->first.c_str();
NOT_AVAIBLE_MIX_DEV[tmpVal] = it->second;
}
Решение банальное, но лутшего способа пока что не вижу...
Сначала заполняем map тот что std::string а потом нормальные значения уже сново через цикл запихиваем уже во второй map но уже с const char*.Все равно до конца так и не понял почему не можно было сделать за один проход...
P.S. Я в C++ новичек..
>[оверквотинг удален]
> }
>
> for (std::map<std::string, int>::iterator it = tmpCont.begin(); it != tmpCont.end(); ++it) {
> const char * tmpVal = it->first.c_str();
> NOT_AVAIBLE_MIX_DEV[tmpVal] = it->second;
> }
>Решение банальное, но лутшего способа пока что не вижу...
>Сначала заполняем map тот что std::string а потом нормальные значения уже сново
>через цикл запихиваем уже во второй map но уже с const
>char*.А почему такое странное требование - получить в результате именно std::map<char *, int>? Если можно это требование убрать, то можно использовать std::map<std::string, int> полученный после первого цикла. Действительно, зачем нужен второй цикл?
Хм, если подумать немного, то, вообще говоря, std::map<char *, int> в большинстве случаев вообще не имеет смысла. Мап существует для того чтобы в него класть данные с ключами и потом находить положенные данные по этим ключам. Если ключ - значение указателя (т.е, 32- или 64-битовое число), то содержание строки на которую указывает этот указатель никакого отношения к тому что хранится в самом мапе не имеет. В мапе хранится именно это число как ключ, а не строка на которую оно указывает.
В общем, мне кажется вы тут чего-то перемудрили. Второй цикл тут явно не нужен - более того, у меня есть сомнения не только по поводу std::map<char *, ...>, но и по поводу правомочности сохранения в мапе указателя который возвращает c_str().
> А почему такое странное требование - получить в результате именно std::map<char *, int>?Все просто, не раз сталкивался с тем, что приходилось все время переводить потом с std::string в const char*.
Вот к примеру мой случай:QLabel *mixDevLabel = new QLabel;
mixDevLabel -> setText ( DEV_NAME );
Благо что DEV_NAME const char*, а то пришлось бы переводить. И таких случаев много.
Заметил что многие API куда чаще используют const char* в замен std::string.
О части из-за того что std::string нету в C?> Мап существует для того чтобы в него класть данные с ключами и потом находить положенные данные по этим ключам.
У меня в мапе привязаное имя устройства к его идентификатору в системе, и оно не обезательно идет по порядку, то есть может быть так:
vol => 2
pcm => 4
gain =>7> у меня есть сомнения не только по поводу std::map<char *, ...>, но и по поводу правомочности сохранения в мапе указателя который возвращает c_str().
А что не так?
Но как бы там небыло, у меня работает :))
>Заметил что многие API куда чаще используют const char* в замен std::string.
>
>О части из-за того что std::string нету в C?Скорее потому что многие C++ API были задуманы до того как STL получила распостранение.
>У меня в мапе привязаное имя устройства к его идентификатору в системе,
>и оно не обезательно идет по порядку, то есть может быть
>так:
>vol => 2
>pcm => 4
>gain =>7Ну да, как я и думал: на логическом уровне, это мап из строки (содержания строки) в некие данные, тут - в число.
>
>> у меня есть сомнения не только по поводу std::map<char *, ...>, но и по поводу
>> правомочности сохранения в мапе указателя который возвращает c_str().
>
>А что не так?
>Но как бы там небыло, у меня работает :))Как я уже говорил, std::map<char *, ...> - это мап не из содержания строки в некие данные, а из значения указателя в некие данные.
Пример:
std::map<char *, int> myMap;
myMap["vol"] = 1;
myMap["pcm"] = 2;
std::cout << myMap["vol"] << "\n"
std::cout << myMap["pcm"] << "\n"Как вы думаете что это напечатает?
Отметьте что, вообще говоря, первое упоминание литерала "vol" в программе совсем не обязяно генерить то-же самое значение указателя что и второе. То есть, когда вы написали
myMap["vol"] = 1;
это компилятор перевел в
static char *literal1 = "vol"; // literal1 == 0x12345678,
myMap[0x12345678] = 1;a когда вы написали
std::cout << myMap["vol"] << "\n"
это компилятор может перевести в
static char *literal2 = "vol"; // literal2 == 0x87654321
std::cout << myMap[0x87654321] << "\n"т.e. literal1 совсем не обязян совпадать с literal2.
Значит, вполне вероятно что
std::cout << myMap["vol"] << "\n"
напечатает какое-то случайное значение (потому что std::string::operator[] автоматически создаст новый элемент в мапе, но его значение, скорее всего, будет случайное целое число).
По поводу c_str(): это возвращает указатель на строку которая живет только до вызова первого non-const метода обьекта std::string. Нужно будет очень внимательно программировать чтобы избежать случайного доступа по 'висячему' указателю. Намного проще и надежнее прямо сразу скопировать данные - что и получится автоматически если использовать sts::map<std::string, int>.