Как создать в классе список, который имеет несколько значений?
Грубо говоря, сделать что-то, на подобие:
List array = new List();
array.Add(4,5,4.3,5);
Console.WriteLine(array[0][2]);
Ответы
Ответ 1
Tuple
List> array = new List>();
array.Add(Tuple.Create(4,5,4.3,5));
Console.WriteLine(array[0].Item3);
ValueTuple:
List<(int,int,double,int)> array = new List<(int,int,double,int)>();
array.Add((4,5,4.3,5));
Console.WriteLine(array[0].Item3);
Есть основное приложение WinForms в котором ведётся парсинг интернет ресурса с записью
необходимых данных в двумерный массив string[5000, 50].
Где 1й столбец строки это ссылка, а остальные столбцы данные с ресурса.
Как только набирается строка c данными , данные этой строки сгруппировано выводятся
в окне приложения.
Как мне эти данные передавать в массив другого приложения WinForms по интернету и
так же выводить ?
Главный вопрос, как передавать эти данные c записью в массив ?
Самостоятельно искал, читал немного про сокеты, но пока каша в голове и не знаю про
подводные камни.
Хотелось бы как то тупо, при заполнении строки массива в главном приложении, сразу
эту строку массива передавать в массив удалённого приложения.
Ответы
Ответ 1
Я мог бы вам предложить один из вариантов организации данного функционала:
Логика :
1) Серверная часть
Ваш Parser после анализа полученных данных и формирования массива значений (рекомендую
использовать коллекции в место статичного массива)
сериализуется в json(или XML формат) строку и добавляет их в базу данных MySQL (локальную
или удалённую) (с одновременной проверкой наличия там таких данных)
- SQL запрос для организации структуры базы данных для ваше случая должна быть примерно
таковой
CREATE DATABASE IF NOT exists parsersystem;
use parsersystem;
CREATE TABLE IF NOT exists Procedures (
number int AUTO_INCREMENT,
id int,
jsonData TEXT,
ts_create timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
ts_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (number)
);
где number это автоинкриментное поле (уникальный номер записи в БД)
id - уникальный специфичный номер записи (заданный пользователем)
jsonData - сериализация любых данных в том числе и массива ваших значений
ts_create - дата и время создание записи
ts_update - дата и время обновления записи
MySQL сервер настраивается таким образом чтобы к нему имели доступ клиенты через
интернет либо локальную сеть (в зависимости от ваших нужд)
2) Клиенты
Клиентское приложение будь то Desktop, Android или другое, не имеет связи непосредственно
с вашим серверным Parser приложением, а обращается к открытой MySQL базе (закрытой
по желанию паролем), на предмет наличия новых записей, это становится возможным при
использовании поля ts_create - которое отражает время создание записи
Логика:
клиент периодически делает запрос к базе данных
примерно такой
SELECT * FROM Procedures WHERE ts_create > TIMESTAMP (время последней
добавленной клиентом записи) ORDER BY ts_create DESC
таким образом клиент получит только новые записи из таблицы
на стороне клиента происходит десереализация jsondata и формирование своей локальной
копии массива (идентичного тому что на стороне сервера)
клиенту следует хранить дату и время последней добавленной записи для последующих
запросов
Преимущество такого подхода :
независимость клиентской и серверной части приложения, (данные хранятся отдельно в БД)
нет необходимости в организации специфического протокола общения (сервер -клиент)
при использовании подхода передачи при помощи сокетов
Предположим что мы имеем некий класс описывающий модель наших данных
public class Procedure(){
public String Name { get; set; }
public List Data { get; set; }
}
А также мы на стороне сервера имеем набор данных (коллекцию)
public List procedures;
Для записи в базу данных такой конструкции стоит применять примерно такой метод:
public AddProceduresToDataBase(List procedureList) {
string Connect = "Port=3306; Database=parsersystem;Data Source=127.0.0.1;Password=mysql_password;
MySqlConnection myConnection = null;
try
{
myConnection = new MySqlConnection(Connect);
myConnection.Open();
}
catch (Exception e)
{
myConnection.Close();
Console.Out.WriteLine("Ошибка доступа к базе данных");
}
foreach (var proc in procedureList)
{
var json = new JavaScriptSerializer().Serialize(proc);
// Экранируем спец символы
json = json.Replace(Convert.ToChar(0171) + "", "")
.Replace(Convert.ToChar(0187) + "", "")
.Replace("\\\"", "")
.Replace("\\","\\\\");
MySqlCommand myCommand_add = new MySqlCommand(@"INSERT INTO Procedures(id,jsondata,ts_create,ts_update)
VALUE (" + сгенерить нужно ID + "+ json + "',null,null);", myConnection);
myCommand_add.ExecuteNonQuery();
}
myConnection.Close(); //Обязательно закрываем соединение!
}
Для чтения коллекции из базы данных стоит использовать примерно такой метод
public List GetProcedures() {
string Connect = "Port=3306; Database=parsersystem;Data Source=127.0.0.1;Password=mysql_password;
MySqlConnection myConnection = null;
try
{
myConnection = new MySqlConnection(Connect);
myConnection.Open();
}
catch (Exception e)
{
myConnection.Close();
Console.Out.WriteLine("Ошибка доступа к базе данных");
}
string CommandText = "SELECT * FROM Procedures ";
MySqlCommand myCommand = new MySqlCommand(CommandText, myConnection);
MySqlDataReader MyDataReader;
MyDataReader = myCommand.ExecuteReader();
List procedures = new List();
String jsonData;
while (MyDataReader.Read())
{
try
{
jsonData = MyDataReader.GetString(2); // Читаем jsondata из 2го столбца
таблицы
Procedure newProcedure = new JavaScriptSerializer().Deserialize(jsonData);
procedures.Add(newProcedure);
}
catch
{
Console.Out.WriteLine("Ошибка разбора JSON данных о процедуре");
}
}
MyDataReader.Close();
myConnection.Close();
return procedures;
}
ПС. естественно методы нужно дорабатывать, внедрить проверку на наличие уже такого
рода данных в БД, расширить функционал чтения для получения новых записей , но это
я уже оставляю вам.
ПС. так же тут в принципе показаны примеры сериализации и десиарилизации данных в JSON
Ответ 2
Хотя автор вопроса уже сделал свой выбор не дождавшись других вариантов. Я все-таки
попробую составить конкуренцию :)
Дамы и господа, вашему вниманию предлагается модный и молодежный Firebase от известнейшей
корпорации добра! Шутка :)
Все хипстеры, попивая смузи в Жан-Жаке, мечтают таки написать свой собственный кошерный
(с шахматами и девочками) чатик для Айфона. Ну, и конечно, ничего кроме Firebase ими
просто не берется в расчет. Потому, если вы даже почти шепотом посмеете сказать или
даже намекнуть про какой-нибудь мускул или подобное, то в ответ вы услышите лишь презрительное
фи, хлопок закрывающегося макбука и досвидос. Опять была шутка :)
Работает это так
Зарегистрируйтесь, зайдите в консоль управления, добавьте новый проект
Слева в меню перейдите к разделу БД
Зайдите в правила
Измените на открытый доступ так
Да, я знаю, что это страшно и небезопасно. Но, мы же только учимся...
1) Создание библиотеки для работы с Firebase
Создайте проект библиотеки, и в нем класс Пакета такой
public class Package
{
public DateTime Date { get; set; }
public string Content { get; set; }
//ctor
public Package()
{
Date = DateTime.Now;
Content = "";
}
public override string ToString()
{
return $"{Date.ToLongTimeString()}: {Content}";
}
public override bool Equals(object obj)
{
var package = obj as Package;
return package != null &&
Date == package.Date &&
Content == package.Content;
}
public override int GetHashCode()
{
var hashCode = -165655869;
hashCode = hashCode * -1521134295 + Date.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Content);
return hashCode;
}
}
Для работы с сабжем создадим класс FirebaseClient
public class FirebaseClient
{
private enum HttpVerb
{
GET,
POST,
PATCH,
DELETE
}
private const string _CONTENT_TYPE = "application/json";
private const string _CONTENT_TYPE_WITH_CHARSET = "application/json: charset=utf-8";
private const string _EMPTY_ANSWER_FIREBASE = "null";
private readonly string _path;
private WebRequest _webRequest;
//ctor
public FirebaseClient(string path)
{
if (String.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
_path = path;
}
///
/// Добавление новых данных
///
/// текст в формате Json
///
public string Insert(string json)
{
if (String.IsNullOrEmpty(json)) throw new ArgumentNullException(nameof(json));
SetWebRequest(HttpVerb.POST);
WriteJsonToStream(json);
return GetResultAfterPostOrPatch();
}
///
/// Обновление данных
///
/// текст в формате Json
///
public string Update(string json)
{
if (String.IsNullOrEmpty(json)) throw new ArgumentNullException(nameof(json));
SetWebRequest(HttpVerb.PATCH);
WriteJsonToStream(json);
return GetResultAfterPostOrPatch();
}
private void SetWebRequest(HttpVerb httpVerb)
{
_webRequest = WebRequest.CreateHttp(_path);
switch (httpVerb)
{
case HttpVerb.GET:
_webRequest.Method = HttpVerb.GET.ToString();
_webRequest.ContentType = _CONTENT_TYPE_WITH_CHARSET;
break;
case HttpVerb.POST:
_webRequest.Method = HttpVerb.POST.ToString();
_webRequest.ContentType = _CONTENT_TYPE;
break;
case HttpVerb.PATCH:
_webRequest.Method = HttpVerb.PATCH.ToString();
_webRequest.ContentType = _CONTENT_TYPE;
break;
case HttpVerb.DELETE:
_webRequest.Method = HttpVerb.DELETE.ToString();
_webRequest.ContentType = _CONTENT_TYPE;
break;
default:
throw new ArgumentException(nameof(httpVerb));
}
}
private void WriteJsonToStream(string json)
{
byte[] buffer = Encoding.UTF8.GetBytes(json);
_webRequest.ContentLength = buffer.LongLength;
_webRequest.GetRequestStream().Write(buffer, 0, buffer.Length);
}
private string GetResultAfterPostOrPatch()
{
string result = String.Empty;
var response = _webRequest.GetResponse();
result = (new StreamReader(response.GetResponseStream())).ReadToEnd();
_webRequest = null;
return result;
}
///
/// Получение данных
///
///
public string Get()
{
string result = String.Empty;
SetWebRequest(HttpVerb.GET);
using (HttpWebResponse response = (HttpWebResponse)_webRequest.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
result = GetStringResponse(response);
}
}
//если в базе нет данных в ответ приходит текст:"null"
if (result == _EMPTY_ANSWER_FIREBASE)
{
result = String.Empty;
}
_webRequest = null;
return result;
}
private string GetStringResponse(HttpWebResponse response)
{
using (Stream responseStream = response.GetResponseStream())
{
if (responseStream == null) return String.Empty;
using (StreamReader reader = new StreamReader(responseStream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
///
/// Удаление данных
///
public void Remove()
{
SetWebRequest(HttpVerb.DELETE);
var response = _webRequest.GetResponse();
_webRequest = null;
}
}
Установите нугет пакет Newtonsoft.Json в нашу библиотеку, напишем класс для отправки
сообщений
public class SenderService
{
private readonly FirebaseClient _service;
//ctor
public SenderService(FirebaseClient service)
{
_service = service ?? throw new ArgumentNullException(nameof(service));
}
public string SendPackage(Package package)
{
if (package == null) throw new ArgumentNullException(nameof(package));
string json = Newtonsoft.Json.JsonConvert.SerializeObject(package);
return _service.Insert(json);
}
public string UpdatePackage(Package package)
{
if (package == null) throw new ArgumentNullException(nameof(package));
string json = Newtonsoft.Json.JsonConvert.SerializeObject(package);
return _service.Update(json);
}
}
А это класс для получения сообщений
public class ReceiverService
{
private readonly FirebaseClient _service;
//ctor
public ReceiverService(FirebaseClient service)
{
_service = service ?? throw new ArgumentNullException(nameof(service));
}
public IEnumerable ReceivePackage()
{
List result = new List();
string answer = _service.Get();
if (String.IsNullOrEmpty(answer)) return result;
//парсинг ответа
JObject jObject = JObject.Parse(answer);
var children = jObject.Children().ToArray();
foreach (var token in children)
{
var package = token.Children()
.First()
.ToObject();
result.Add(package);
}
return result;
}
}
2) Приложение для отправки сообщений
Я написал WPF, приведу только ViewModel
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
///
/// Внимание! Этот адрес через пару дней я удалю.
/// Создайте свой собственный с правилами:
/// {
/// "rules": {
/// ".read": true,
/// ".write": true
/// }
/// }
/// И не забудьте в конце адреса дописать .json
///
private readonly string _firebase = "https://testtodo-8d83d.firebaseio.com/.json";
private int _sendIndex = 1;
//ctor
public MainViewModel()
{
}
private string _InputText = "Сообщение";
public string InputText
{
get { return _InputText; }
set
{
_InputText = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InputText)));
}
}
private string _OutputText;
public string OutputText
{
get { return _OutputText; }
set
{
_OutputText = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OutputText)));
}
}
private bool _IsIndexed = true;
public bool IsIndexed
{
get { return _IsIndexed; }
set
{
_IsIndexed = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsIndexed)));
}
}
public void SendOnePackage()
{
if (String.IsNullOrEmpty(InputText))
{
OutputText = "Невозможно отправить пустое сообщение";
return;
}
if (IsIndexed)
{
var chars = InputText.ToCharArray()
.Where(c => !char.IsDigit(c))
.ToArray();
InputText = new string(chars) + _sendIndex.ToString();
_sendIndex++;
}
Package package = new Package { Content = InputText };
SenderService service = new SenderService(new FirebaseClient(_firebase));
var answer = service.SendPackage(package);
answer = answer.Substring(9, answer.Length - 11);
OutputText = $"Получен и сохранен с именем: {answer}";
}
}
3) Приложение получатель
Тоже только ViewModel
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
///
/// Внимание! Этот адрес через пару дней я удалю.
/// Создайте свой собственный с правилами:
/// {
/// "rules": {
/// ".read": true,
/// ".write": true
/// }
/// }
/// И не забудьте в конце адреса дописать .json
///
private readonly string _firebase = "https://testtodo-8d83d.firebaseio.com/.json";
private readonly IMainWindow _mainWindow;
private volatile bool _shouldHearing;
private SynchronizationContext _context = SynchronizationContext.Current;
private HashSet _hashSet;
private int _countElements;
//ctor
public MainViewModel(IMainWindow mainWindow)
{
_mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow));
}
private ObservableCollection _Messages;
public ObservableCollection Messages
{
get { return _Messages; }
set
{
_Messages = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Messages)));
}
}
///
/// Начать слушать
///
public void DoHear()
{
_shouldHearing = true;
_hashSet = new HashSet();
_countElements = 0;
Task.Run(() => Hearing());
}
private void Hearing()
{
while (_shouldHearing)
{
var service = new ReceiverService(new FirebaseClient(_firebase));
IEnumerable packages = service.ReceivePackage();
if (packages.Count() == 0) continue;
_context.Post(delegate { AddPackages(packages); }, null);
}
}
private void AddPackages(IEnumerable packages)
{
foreach (var package in packages)
{
_hashSet.Add(package);
}
if (_countElements < _hashSet.Count)
{
Messages = new ObservableCollection(_hashSet);
_countElements = _hashSet.Count;
}
}
///
/// Остановка
///
public void StopHearing()
{
_shouldHearing = false;
if (Messages?.Count > 0)
{
_mainWindow.ChangeStateControls(StateControls.MessagesExists);
}
else
{
_mainWindow.ChangeStateControls(StateControls.Startup);
}
}
///
/// Удаление ранее отправленных
///
public void RemovePackages()
{
if (Messages.Count == 0) return;
var client = new FirebaseClient(_firebase);
client.Remove();
Messages.Clear();
_mainWindow.ChangeStateControls(StateControls.Startup);
}
}
Весь пример лежит здесь.
У меня есть код, который выводит в консоль некоторый текст:
int main()
{
std::cout << "Hello, world!";
// Обеспечим некоторую задержку, не давая приложению завершиться
pause();
return 0;
}
Однако текст не выводится, пока приложение не завершится штатным образом (в данном
случае при сигнале).
В чём дело?
Ответы
Ответ 1
Вы столкнулись с таким явлением, как буферизация потоков ввода-вывода.
Дело в том, что в C++ потоки std::istream и std::ostream (и, сответственно, их потомки)
— это всего лишь обёртки над побайтовым буфером std::streambuf. Сам же std::streambuf
содержит в себе два кэша — один для чтения, другой для записи, — в виде массивов, описываемых
границами и курсором (то есть текущей позицией):
Источник: Frank B. Brokken. C++ Annotations Version 10.8.1. Chapter 14: Polymorphism
При любой работе с данными класс буфера сначала пытается сделать всё в рамках этого
кэша. И только если это не является возможным (переполнился буфер записи, либо исчерпался
буфер чтения), он обращается к своему дочернему классу-реализации конкретного буфера
и просит либо опустошить буфер записи, либо загрузить новые данные для чтения (через
вызов чисто виртуальных методов overflow() и underflow() соответственно).
В вашем случае очистка кэша срабатывала исключительно при уничтожении std::streambuf,
ассоциированного со стандартным потоком вывода (так как длина записанной строки была
недостаточной для переполнения кэша). Соответственно, для немедленного вывода данных
необходимо сбросить буфер записи вручную, вызовом метода std::ostream::flush(). Кстати,
этот метод неявно вызывается при использовании std::endl.
Здравствуйте, возник вопрос с тем, как узнать разрядность ОС Windows. Пробовал через
препроцессинг, но выдает неправильные данные...
#ifdef WIN64
#define OS_BIT 64
#endif // !WIN64
#ifdef WIN32
#define OS_BIT 32
#endif // !WIN32
Может быть есть какой-нибудь другой способ?
Ответы
Ответ 1
BOOL Is64BitWindows()
{
#if defined(_WIN64)
return TRUE; // Программа скомпилирована для x64
#elif defined(_WIN32)
// Программа скомпилирована для x32, спрашиваем ОС
BOOL f64 = FALSE;
return IsWow64Process(GetCurrentProcess(), &f64) && f64;
#else
return FALSE; // Программа скомпилирована для x16
#endif
}
Источник: https://blogs.msdn.microsoft.com/oldnewthing/20050201-00/?p=36553/
Ответ 2
Можно попытаться узнать путь к папке Wow64. На 32-битных системах этот вызов всегда
возвращает FALSE и код ошибки будет ERROR_CALL_NOT_IMPLEMENTED. Привилегированных прав
для вызова не требуется, должна работать начиная с WindowsXP (на 10 работает точно).
bool Is64bitWindows(void) noexcept
{
auto is_64_bit{true};
if(FALSE == GetSystemWow64DirectoryW(nullptr, 0u))
{
auto const last_error{::GetLastError()};
if(ERROR_CALL_NOT_IMPLEMENTED == last_error)
{
is_64_bit = false;
}
}
return(is_64_bit);
}
Подскажите, пожалуйста, расширение или что-то подобное, что сможет сохранить мои
личные CSS для сторонних сайтов. К примеру, у ВКонтакте background: #edeef0;, а я хочу
поменять на свой цвет, чтобы при перезагрузке страницы дописывались стили через !important
или inline.
Ответы
Ответ 1
Расширения для браузера - Stylish
Ответ дан пользователем: diraria.
Ответ 2
Использую User JavaScript and CSS для Хрома.
Шикарная штука
Ответ 3
Если требуются только стили без скриптов, то подойдёт User CSS - можно настраивать
стили для конкретных сайтов, включать или выключать расширение. Использую его для некоторый
модификаций SO.
К сожалению, это расширение добавляет стили только после загрузки страницы. В связи
с сезоном шляп пришлось сменить его на другое.
Ответ 4
Расширение Stylebot добавляет стили немедленно, не дожидаясь загрузки страницы, что
очень удобно в случаях, когда требуется немедленное применение стилей (пример).
Только надо переключить режим редактирования в Edit CSS:
У меня есть 5 инпута, нужно сделать так, чтобы если одно из них не заполнить, то
сообщение не должно быть отправлено.
document.querySelector(".btn-info").addEventListener("click", function(){
var required = document.querySelectorAll(".required");
if (required.val == "") {
alert("please fill all the columns");
}
});
.required {
display:block;
padding-bottom:10x;
}
Ответы
Ответ 1
Так как querySelectorAll возвращает коллекцию элементов, то проверка required.val
== "" бессмысленна, ибо берется val (которого и одиночного-то элемента нет), которого
в коллекции нет. Соответственно можно пробежать по коллекции и просмотреть значение
value у элементов:
document.querySelector(".btn-info").addEventListener("click", function(){
let required = document.querySelectorAll(".required");
let isAllFilled = true;
for (let i = 0; i < required.length; ++i) {
if (required[i].value.trim() == "") {
isAllFilled = false;
break;
}
}
if (!isAllFilled)
alert("please fill all the columns");
});
.required {
display:block;
padding-bottom:10x;
}
Ответ 2
Альтернативным решением может стать использование атрибута required и псевдокласса
:invalid в селекторе.
В этом случае достаточно будет проверить, что такие элементы есть.
Пример:
document.querySelector(".btn-info").addEventListener("click", function() {
var required = document.querySelectorAll(".required:invalid");
if (required.length) {
console.log("please fill all the columns");
}
});
.required {
display: block;
padding-bottom: 10x;
}
Зачем нужен спецификатор private в C++, если по умолчанию итак все функции и переменные
являются закрытыми?
Ответы
Ответ 1
Например, потому, что в структуре по умолчанию все функции и поля являются открытыми.
Кроме того, текстовый порядок полей влияет на порядок их инициализации в конструкторе!
Поэтому просто так переставлять поля класса нельзя, это может изменить смысл программы.
Пример. Допустим, что мы хотим, чтобы поле len было открытым, а поле data — закрытым.
class MyString
{
public:
unsigned len;
private:
char* data;
public:
MyString(const char* s) :
len(std::strlen(s)),
data(new char[len + 1])
{
std::memcpy(data, s, len + 1);
}
// ...
};
Мы не можем переставить местами len и data, потому что инициализаторы в конструкторе
выполняются в том порядке, в котором указаны поля, а не в том порядке, в котором они
написаны в коде! А значит, без private не обойтись.
Ответ 2
Нет, конечно, можно сначала в классе описывать закрытые члены и закрытые функции,
а только потом открытые... И никогда не пользоваться struct, если нужен хоть один закрытый
член...
Но, откровенно говоря, насколько же это неудобно - при изменении доступа нужно перемещать
член в классе, интерфейс нужно всегда показывать только после внутренностей класса...
Нет, пожалуй, все же удобство запоминания на одно ключевое слово меньше не в состоянии
перевесить все преимущества, этим ключевым словом предоставляемые, нет? :)
Если я выделяю память для некоторого типа T - пусть это в шаблоне, скажем -
template
char* alloc(size_t n) { return new char[n*sizeof(T)] ; }
то места гарантированно хватит для n элементов с типом T или нет? Какие-то проблемы
из-за выравнивания могут быть? И если могут - то как их правильно решить?
Дополнение - по совету @VladD я ппробовал выделить память с особыми требованиями
к выравниванию:
#include
#include
using namespace std;
struct alignas(256) D {
char a;
char b;
};
template
void* alloc(size_t n) {
return reinterpret_cast(new
aligned_storage[n]);
}
template
int allocSize() {
cout << "sizeof = " << sizeof(T) << ", alignof = " << alignof(
T) << endl;
cout << "type = " << typeid(typename
aligned_storage::type).name()
<< endl;
return sizeof(typename
aligned_storage::type);
}
int main() {
cout << allocSize() << endl;
cout << alloc(5) << endl;
}
GCC это не смог отработать - https://ideone.com/dgo75h. Но Visual Studio нормально
отработал, но выдал:
sizeof = 256, alignof = 256
type = union std::_Align_type
256
004A1258
т.е. понял и размер, и выравнивание, но адрес дал выровненный явно не на 256. Мне
это выравнивание не нужно, это так, игры - но тем не менее получается, что так выделять
память не получается?
Ответы
Ответ 1
По идее, вам нужно std::aligned_storage.
Должно по идее работать так:
template
char* alloc(size_t n)
{
return new aligned_storage[n];
}
Проблемы из-за выравнивания с простым подходом очевидны: new char[] не знает ваших
требований по выравниванию, и может выделить неправильно выровненную память.
Для выделения объектов в вашей памяти вам понадобится placement new. Вам должен пригодится
std::align, использование которого описано здесь.
Обновление: я перепроверил, и на самом деле new не соблюдает кастомные требования
по выравниванию. Поэтому нужно делать выравнивание самостоятельно, при помощи std::align.
Получается вот что:
// подсчёт длины нужной аллокации
template
size_t aligned_char_size(size_t n)
{
size_t result = n * sizeof(T);
bool is_overaligned = alignof(T) > alignof(std::max_align_t);
if (is_overaligned)
result += alignof(T) - 1;
return result;
}
Применяем:
struct alignas(1024) S
{
};
// выделёем память на 3 экземпляра
size_t total_space = aligned_char_size(3);
void* p = new char[total_space];
std::cout << p << std::endl;
// получаем выровненный указатель при помощи std::align
void* allocated_item = p;
size_t remaining_space = aligned_char_size(3);
if (std::align(alignof(S), sizeof(S), allocated_item, remaining_space))
{
std::cout << allocated_item << std::endl;
std::cout << (size_t)allocated_item % alignof(S) << std::endl;
}
else
{
std::cout << "Impossible" << std::endl;
}
Результат на Visual Studio 2017:
000002333DF15950
000002333DF15C00
0
Если убрать alignas(1024), получаем
000002A27FD2ECD0
000002A27FD2ECD0
0
Ответ 2
Нет, проблем не будет.
Во-первых, места гарантированно хватит.
Во-вторых, касательно выравнивания new-expression гарантирует, что выделенная через
new char[n] память выровнена по самому строгому фундаментальному требованию выравнивания.
То есть память будет выровнена по alignof(std::max_align_t). Полученная от new-expression
память будет правильно выровнена для хранения любых фундаментальных типов, поддерживаемых
реализацией, и типов, построенных из них.
3.7.4.1 Allocation functions
2 [...] The pointer returned shall be suitably aligned so that it can be converted
to a pointer to any suitable complete object type (18.6.2.1) and then used to access
the object or array in the storage allocated (until the storage is explicitly deallocated
by a call to a corresponding deallocation function).[...]
5.3.4 New
11 When a new-expression calls an allocation function and that allocation has not
been extended, the new-expression passes the amount of space requested to the allocation
function as the first argument of type std::size_t. That argument shall be no less
than the size of the object being created; it may be greater than the size of the object
being created only if the object is an array. For arrays of char and unsigned char,
the difference between the result of the new-expression and the address returned by
the allocation function shall be an integral multiple of the strictest fundamental
alignment requirement (3.11) of any object type whose size is no greater than the size
of the array being created. [ Note: Because allocation functions are assumed to return
pointers to storage that is appropriately aligned for objects of any type with fundamental
alignment, this constraint on array allocation overhead permits the common idiom of
allocating character arrays into which objects of other types will later be placed.
—end note ]
Приведенная цитата открытым текстом говорит, что ограничения на структуру блока памяти,
выделяемого через new char[n] были введены именно для того, чтобы обеспечить правильное
выравнивание при использовании именно вашего способа выделения памяти.
Поэтому беспокоиться о выравнивании вам не нужно, пока у вас не появляются какие-то
специальные требования расширенного выравнивания, превосходящие максимальное фундаментальное
выравнивание.
Есть необходимость из таблицы со столбцом:
ID
1
3
6
8
9
11
23
... получить таблицу:
ID Res
1 1
3 1,3,1
6 1,3,6,3,1
8 1,3,6,8,6,3,1
9 1,3,6,8,9,8,6,3,1
11 1,3,6,8,9,11,9,8,6,3,1
Понимаю, что звучит бредово, но такие были поставлены условия: создать запрос без
использования Model, аналитических функций, регулярных выражений, иерархических запросов
и рекурсивного WITH. Есть ли какой-нибудь способ решить эту задачу при данных условиях?
Очень прошу, если вы знаете (хотя бы примерно), как это можно сделать, поделиться
методом или названиями функций, которые могут быть применены.
P.S. PL/SQL не рассматривается
Ответы
Ответ 1
Перемножаем таблицу на саму себя, по такому условию, что бы к ID были выбраны все
остальные, которые меньше его. После этого остается сгруппировать и собрать строку
ID через запятую в нужном порядке (два раза, в прямом и обратном направлении):
with Tab(ID) as(
select 1 from DUAL union all select 3 from DUAL union all select 6 from DUAL union
all select 8 from DUAL union all
select 9 from DUAL union all select 11 from DUAL union all select 23 from DUAL
)
select A.ID, trim(both ',' from
LISTAGG(B.ID, ',') WITHIN GROUP (order by B.ID) || ',' ||
A.ID || ',' ||
LISTAGG(B.ID, ',') WITHIN GROUP (order by B.ID desc) )
from Tab A
left join Tab B on B.ID < A.ID
group by A.ID
order by A.ID
Тест на sqlfiddle.com
Ответ 2
Похожий вариант, но с одним использованием listagg и без лишних запятых:
select t.id, LISTAGG(q.id, ',') WITHIN GROUP (ORDER BY q.ord, q.id * q.ord2)
from test t
join (
select id, 1 ord, 1 ord2
from test
union
select id, 2, -1 ord2
from test
) q on t.id >= q.id and q.ord = 1 or t.id > q.id
group by t.id
order by t.id;
SqlFiddle
Есть dll без исходников, для нее есть интерфейс на delphi. Стоит задача подружить
эту библиотеку с 1С. На сколько понял с прочитанного в интернете, на основе интерфейса
можно сделать dll по COM технологии.
Пример интерфейса:
unit Unit1;
interface
uses Windows,
Messages,
Classes,
SysUtils,
Forms;
type
TBonusCountersPrintQuery = packed record
Card: array[0..15] of char;
end;
TListCountersPrint = packed record
Lines: array[0..PACKET_COUNTERS_PR_MAX_LINE_COUNT - 1] of TCounterLine;
end;
TCounterLine = packed record
LineNo: word;
IsLast: byte;
Num_Counter: word;
N_Counter: array[0..9] of char;
Value_Counter: Cardinal;
end;
type
THWE_ProcessBonusListCountersPrint = function(vBonusCountersPrintQuery: TBonusCountersPrintQuery;
var vListCountersPrint: TListCountersPrint): Integer; stdcall;
function ProcessBonusListCountersPrint(vBonusCountersPrintQuery: TBonusCountersPrintQuery;
var vListCountersPrint: TListCountersPrint): Integer; stdcall;
implementation
var
HWE_ProcessBonusListCountersPrint : THWE_ProcessBonusListCountersPrint;
function init_dynamic(ALibrary: string): Boolean;
begin
hLib := SafeLoadLibrary(ALibrary);
Result := (hLib <> 0);
if Result then
begin
@HWE_ProcessBonusListCountersPrint := GetProcAddress(hLib, 'ProcessBonusListCountersPrint');
Result := Assigned(HWE_ProcessBonusListCountersPrint);
end;
end;
function ProcessBonusListCountersPrint(vBonusCountersPrintQuery: TBonusCountersPrintQuery;
var vListCountersPrint: TListCountersPrint): Integer;
begin
result := HW_FAILURE;
if hLib <> 0 then result := HWE_ProcessBonusListCountersPrint(vBonusCountersPrintQuery,
vListCountersPrint);
end;
initialization
finalization
end.
Как правильно написать такую dll, чтобы функция из примера была доступна из 1С?
Ответы
Ответ 1
Ваша библиотека будет связующим звеном между 1C и сторонней библиотекой без исходного
кода:
1C -> ВашаБиблиотекаCOM -> СтороняяБиблиотека
То, что вы описали - попытка реализовать связь:
ВашаComБиблиотека -> СтороняяБиблиотека
Осталось реализовать связь между 1C и вашей библиотекой.
Чтобы начать разработку библиотеки по технологии COM в Delphi нужно выбрать: File
-> New -> Other и там найти ActiveX Library, новый проект будет выглядить приблизительно так:
Далее к проекту добавить COM-сервер: File -> New -> Other и найти COM Object, выглядеть
в новых версиях Delphi будет как-то так:
В старых версиях Delphi так:
В полях ClassName (CoClassName) нужно ввести английское название, по которому 1C
и будет загружать вашу библиотеку после регистрации в системе, например: MaximLibrary1C
Подробнее о пошаговом создании COM-библиотеки можете почитать здесь: http://www.introligator.org/articles/3/78
После написания прослойки (куда вы перенесете свой вышеописанный код) и окончания
разработки на целевой машине вашу новую .dll нужно зарегистрировать в системе, как-то так:
regsvr32 c:\Library\Project1.dll
Конечно, имя проекта лучше задать более вменяемое, а не Project1.
На машине с Delphi зарегистрировать можно из среды в меню "Run".
Из самой 1C вашу библиотеку после регистрации в системе можно будет загружать как-то так:
МояБиблиотека = Новый COMОбъект("MaximLibrary1C");
// вызываем метод в библиотеке
МояБиблиотека.1C_ProcessBonusListCountersPrint();
Ответ 2
Попробуйте технологию Native API – собственный интерфейс системного программирования
1С:Предприятия 8.
1CNativeLib - библиотека Delphi для создания внешних компонент (ВК) 1С по технологии
Native API. Позволяет заниматься непосредственно функционалом Внешней Компоненты, не
отвлекаясь на соблюдение инструкций, предоставленных фирмой 1С.
Внешняя компонента NativeAPI на Delphi Инфостарт
Внешняя компонента собранная на библиотеке, указанной вше
Ответ 3
Проблема решена. Очень помогла книга "Delphi и 1С - Предприятие. Программирование
информационного обмена" Попов С.А. 2007 год.
В Delphi, в том числе в последних версиях (могу проверить только до 10.1 Berlin,
хотя негативные отзывы есть и о 10.2 Tokyo), класс TMemoryStream не может выделить
под данные более 2 Гб даже при компиляции в 64-битном режиме. Попытки записать в поток
большие объёмы приводят к ошибке "Out of memory while expanding memory stream".
Можно ли решить эту проблему без создания собственного класса, реализующего поток
в памяти?
Ответы
Ответ 1
Давайте взглянем в определение класса TMemoryStream в файле System.Classes.pas:
TMemoryStream = class(TCustomMemoryStream)
private
FCapacity: Longint;
procedure SetCapacity(NewCapacity: Longint);
protected
function Realloc(var NewCapacity: Longint): Pointer; virtual;
property Capacity: Longint read FCapacity write SetCapacity;
public
destructor Destroy; override;
procedure Clear;
procedure LoadFromStream(Stream: TStream);
procedure LoadFromFile(const FileName: string);
procedure SetSize(const NewSize: Int64); override;
procedure SetSize(NewSize: Longint); override;
function Write(const Buffer; Count: Longint): Longint; override;
function Write(const Buffer: TBytes; Offset, Count: Longint): Longint; override;
end;
Сразу отмечаем, что для адресации памяти используются переменные типа longint, что,
естественно, объясняет невозможность использования более 2 Гб. Более того, если взглянуть
на методы Read (их два) непосредственного предка TMemoryStream - TCustomMemoryStream:
function TCustomMemoryStream.Read(var Buffer; Count: Longint): Longint;
begin
if (FPosition >= 0) and (Count >= 0) then
begin
Result := FSize - FPosition;
if Result > 0 then
begin
if Result > Count then Result := Count;
Move((PByte(FMemory) + FPosition)^, Buffer, Result);
Inc(FPosition, Result);
Exit;
end;
end;
Result := 0;
end;
function TCustomMemoryStream.Read(Buffer: TBytes; Offset, Count: Longint): Longint;
begin
if (FPosition >= 0) and (Count >= 0) then
begin
Result := FSize - FPosition;
if Result > 0 then
begin
if Result > Count then Result := Count;
Move((PByte(FMemory) + FPosition)^, Buffer[Offset], Result);
Inc(FPosition, Result);
Exit;
end;
end;
Result := 0;
end;
, то заметим, что результат может быть посчитан неправильно.
Поэтому решением было: заменить тип переменных внутри TMemoryStream на NativeInt
и Int64, в том числе локальных переменных, а методы TCustomMemoryStream.Read переписать,
например, так:
function TCustomMemoryStream.Read(var Buffer; Count: Longint): Longint;
var
diff: Int64;
begin
if (FPosition >= 0) and (Count >= 0) then
begin
diff := FSize - FPosition;
if diff > 0 then
begin
if diff > Count then
Result := Count
else
Result := diff;
Move((PByte(FMemory) + FPosition)^, Buffer, Result);
Inc(FPosition, Result);
Exit;
end;
end;
Result := 0;
end;
function TCustomMemoryStream.Read(Buffer: TBytes; Offset, Count: Longint): Longint;
var
diff: Int64;
begin
if (FPosition >= 0) and (Count >= 0) then
begin
diff := FSize - FPosition;
if diff > 0 then
begin
if diff > Count then
Result := Count
else Result := diff;
Move((PByte(FMemory) + FPosition)^, Buffer[Offset], Result);
Inc(FPosition, Result);
Exit;
end;
end;
Result := 0;
end;
Это позволяет работать с объёмами памяти более 2 Гб (тестовый пример спокойно обработал
5-гигабайтный файл, полностью скопировав его в поток.
Для тех, кто захочет внести изменения у себя в System.Classes, напомню, что предварительно
необходимо сохранить резервную копию этого файла - на всякий случай :)
Update 1
Для тех, кто по какой-то причине не хочет идти по предложенным выше вариантам, могу
предложить класс TSegmentedMemoryStream - он доступен для скачивания и работает (проверено!).
Ответ 2
В текущей версии Delphi (10.2.2), класс объявлен следующим образом:
TMemoryStream = class(TCustomMemoryStream)
private
FCapacity: NativeInt;
protected
procedure SetCapacity(NewCapacity: NativeInt); virtual;
function Realloc(var NewCapacity: Longint): Pointer; virtual;
property Capacity: NativeInt read FCapacity write SetCapacity;
public
destructor Destroy; override;
procedure Clear;
procedure LoadFromStream(Stream: TStream);
procedure LoadFromFile(const FileName: string);
procedure SetSize(const NewSize: Int64); override;
procedure SetSize(NewSize: Longint); override;
function Write(const Buffer; Count: Longint): Longint; override;
function Write(const Buffer: TBytes; Offset, Count: Longint): Longint; override;
end;
Видно, что они кое-как попытались исправить проблему, но она ещё не до конца исправлена
(например, функция Realloc всё ещё завязана на LongInt).
В квалити-центре есть несколько тикетов касательно TMemoryStream:
(баг, отработан) TCustomMemoryStream and TMemoryStream don't work with very large streams
(баг, не отработан) TMemoryStream Realloc not support Int64 For greater than 2G
(новая фича, не отработан) TMemoryStream does not support large (> 2 Gb) memory allocations
Так что есть надежда, что они наконец сделают в TMemoryStream поддержку 64-х бит.
Пытаюсь реализовать простую нейронную сеть прямого распространения. Но результат,
который она выдает после обучения(методом обратного распространения ошибки) стремиться
к значению 0.5. Хоть после 1000 эпох обучения, хоть после 10000. Она имеет 3 входа,
2 нейрона в скрытом слое и 1 нейрон результирующий.
А если не обучать её, а с только что сгенерированными весами заставить принимать
решение, то её ответ колебается в диапазоне от 0.5 к 1.
Вот код того, что есть на данный момент.
/*var weight_1 = [[0.79, 0.44, 0.43],
[0.85, 0.43, 0.29]],
weight_2 = [[0.5, 0.52]],*/
var weight_1 = randArr(2, 3),
weight_2 = randArr(1, 2),
learning_rate = 0.05,
data = [
[[0,0,0], 0],
[[0,0,1], 1],
[[0,1,0], 1],
[[0,1,1], 0],
[[1,0,0], 1],
[[1,0,1], 0],
[[1,1,0], 0],
[[1,1,1], 1]
];
console.log("---------До тренировки---------");
console.groupCollapsed("Prediction before");
predictSet(data);
console.groupEnd();
function randArr(rows, cols) {
arr = [];
for (var i = 0; i
Ответы
Ответ 1
Возьмём фреймворк для постройки сетей, воссоздадим архитектуру вашей сети и... Увидим
что даже заведомо правильная работающая сеть не может выявить закономерности в ваших
входных данных. Для заведомо отрицательного образца [1,1,0] сеть показывает результат
в пределах 0.5. Если посмотреть на ошибку обучения, то на 20000 итерациях она будет
порядка 0.2, из чего самого по себе можно сделать вывод что архитектуры сети недостаточно.
Вы можете сколько угодно пытаться исправить алгоритм, но у вас ничего не получится
без изменения и усложнения архитектуры. В качестве эксперимента можно попробовать руками
подобрать веса, которые бы давали правильный ответ. Уверен что это невозможно сделать
даже человеку, что уж говорить об алгоритмах, которые даже с горы толком спуститься
не могут.
var myNetwork = new synaptic.Architect.Perceptron(3, 2, 1)
var trainer = new synaptic.Trainer(myNetwork)
var trainingSet = [
{
input: [0,0,0],
output: [0]
}, {
input: [0,0,1],
output: [1]
}, {
input: [0,1,0],
output: [1]
}, {
input: [0,1,1],
output: [0]
}, {
input: [1,0,0],
output: [1]
}, {
input: [1,0,1],
output: [0]
}, {
input: [1,1,0],
output: [0]
}, {
input: [1,1,1],
output: [1]
}
]
var trainingOptions = {
rate: .1,
iterations: 20000,
error: .005,
}
console.log(trainer.train(trainingSet, trainingOptions));
console.log(myNetwork.activate([0,0,1]));
console.log(myNetwork.activate([1,0,0]));
console.log(myNetwork.activate([1,1,1]));
console.log(myNetwork.activate([1,1,0]));
Если усложнить структуру, добавив три нейрона в скрытом слое... То система для того
же отрицательного образца [1,1,0] показывает результат близкий к 0.1, то есть - ожидаемо
негативный. Ошибка обучения минимальная. Это, конечно, не обязательно хорошо, но для
нашего минимального
и исчерпывающего набора данных к ошибке нет вопросов.
(Хватило бы добавить и двух нейронов, но для надёжности - лучше больше.)
var myNetwork = new synaptic.Architect.Perceptron(3, 5, 1)
var trainer = new synaptic.Trainer(myNetwork)
var trainingSet = [
{
input: [0,0,0],
output: [0]
}, {
input: [0,0,1],
output: [1]
}, {
input: [0,1,0],
output: [1]
}, {
input: [0,1,1],
output: [0]
}, {
input: [1,0,0],
output: [1]
}, {
input: [1,0,1],
output: [0]
}, {
input: [1,1,0],
output: [0]
}, {
input: [1,1,1],
output: [1]
}
]
var trainingOptions = {
rate: .1,
iterations: 20000,
error: .005,
}
console.log(trainer.train(trainingSet, trainingOptions));
console.log(myNetwork.activate([0,0,1]));
console.log(myNetwork.activate([1,0,0]));
console.log(myNetwork.activate([1,1,1]));
console.log(myNetwork.activate([1,1,0]));
Ответ 2
Благодаря пользователю sanmai я наконец решил свою проблему! Больше недели стараний,
и вот у меня уже готовая нейронная сеть. Полностью переписал функцию тренировки, оказалось
я использовал неправильные формулы.
Привожу код, может кому-нибудь пригодится
var hiddenLayer = 4,
weight_1 = randArr(hiddenLayer, 2), //(скрытий слой, входной слой)
weight_2 = randArr(1, hiddenLayer), //(выходной слой, скрытый слой)
learning_rate = 1,
data = [
[[0,0], 1],
[[0,1], 0],
[[1,0], 0],
[[1,1], 1]
];
function randArr(rows, cols) {
arr = [];
for (var i = 0; i
This question already has an answer here:
Как упростить проверку на null на C#?
(1 ответ)
Closed 1 год назад.
При просмотре одно из обучающих видео увидел следующую запись:
public event EventHandler Boom;
protected virtual void OnBoom();
{
Boom?.Invoke(this, EventArgs.Empty);
}
А конкретно интересен оператор '?' при вызове метода Invoke на событии. Зачем нужен
данный оператор? Какую роль он выполняет если применяется подобным образом при вызове
методов? Касательно этого куска кода, который некачественно вырезан из контекста, информация
не интересна. Нужно общее предназначение оператора '?'.
Ответы
Ответ 1
Начиная с C# 6.0 в языке появился оператор условного null (Null-Conditional Operator)
или, как его нередко называют, элвис-оператор. Он позволяет упростить проверку на значение
null в условных конструкциях.
И в данном примере, если Boom == null, вызов Invoke не произойдет. Если Boom != null,
то вызов произойдет.
Общее предназначение оператора таково - если переменная, которая стоит перед ?, равна
null, то вызов метода на этом объекте не произойдет.
Когда-то давно в С++ была возможность указать для метода/функции спецификацию nothrow.
Которая указывала, что метод/функция не выбрасывает исключений.
Как дисциплинированный пользователь, я попробовал я в свое время использовать эту
спецификацию. Но вскоре выяснилось, что при разработке очень утомительно отслеживать,
какие функции выбрасывают исключение, а какие нет. И я перестал указывать nothrow.
Сейчас появился проект, в котором принципиально нет генерации исключений. В связи
с этим появились вопросы.
Вопросы:
Поддерживается ли сейчас в стандарте спецификация nothrow?
Рекомендована ли сейчас в стандарте спецификация nothrow? Или, как часто бывает с
С++ с нововведениями, она объявлена устаревшей.
Что дает использование спецификации nothrow? Может ли транслятор генерировать более
быстрый/компактный код при использовании спецификации nothrow? Или никакой разницы нет?
Можно ли сразу указать транслятору, что все методы класса имеют спецификацию nothrow?
Чтобы не возиться с каждым методом и не указывать в нем nothrow.
Ответы
Ответ 1
Вероятно, что Вы говорит о throw(), т.к. std::nothrow это немного другое. Спецификация
throw() помечена как устаревшая (deprecated) в C++17 и будет удалена из будущих стандартов,
поэтому использовать её не рекомендуется. В современном стандарте есть другой спецификатор:
noexcept, суть которого в том, чтобы вызывать std::terminate если исключение покидает
функцию, помеченную этим спецификатором.
С использованием noexcept код может получиться лучше, но основная задача этого спецификатора
задавать соответствующие интерфейсы. Поэтому нет никаких механизмов, которые делают
всё noexcept — каждый интерфейс должен быть явно помечен (за исключением деструкторов,
которые являются noexcept по умолчанию).
Ответ 2
Немного о константе std::nothrow_t nothrow и спецификаторе noexcept
Известно что: Когда operator new не может выделить память, генерирует исключение
std::bad_alloc. Для демонстрации напишем функцию:
template < class T >
void capture1(T** p) throw(std::bad_alloc)
{
int i{};
while (true) {
try {
*p++ = new T[100000]; // когда нибудь в память будет исчерпана
++i;
}
catch (std::bad_alloc& exc) {
std::cout << "На шаге "
<< i << " генерирован " << exc.what() << std::endl;
return;
}
}
}
Но может быть нам понадобится написать такую же функцию, не генерирующую исключение.
И часто, для пользователей удобно знать, что эта функция не генерирует никакого исключения
(мы об этом явно можем сказать в обьявлении функции).
template < class T >
void capture2(T** p) noexcept
{
int i{};
while (true) {
// Если оператор не найдет память, то указатель просто станет нулевым
*p++ = new(std::nothrow) T[100000]; // когда нибудь в память будет исчерпана
++i;
if (*p == 0) {
std::cout << "Память исчерпана на шаге " << i << std::endl ;
return;
}
}
}
И в нашей программе:
//...
int** pi = new int*[100000];
capture1(pi); // на шаге i сгенерировано исключение std::bad_alloc
capture2(pi); // память исчерпанa на шаге i
// дальше освобождения памяти и все другое
Это всего лишь демонстрация того, что и как...
Как при наведении на SVG-элемент реализовать эффект блика (когда осветлённый градиент
проходит через весь элемент). Желательно без SMIL анимации, чтобы анимация касалась,
как иконки, так и текста.
.game_block__play {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(1);
width: 300px;
height: 110px;
outline: none;
background: none;
transition: .2s all;
}
.game_block__play svg {
fill: #FE20A4;
stroke: #2F0E3D;
stroke-width: 2;
stroke-miterlimit: 10
}
.game_block__play text {
font-size: 74px;
font-family: Arial, sans-serif;
transform: translate(110px, 84px);
}
Ответы
Ответ 1
Анимация градиента в зоне действия треугольного значка
Для адаптивности перенёс кнопку из css в svg. Обернул svg контейнером
При такой схеме вёрстки кнопку с анимационными эффектами можно вставить в любое место
страницы HTML и позиционировать, масштабировать по мере своей необходимости.
Анимация начинается по клику begin="svg1.click" при желании можно сделать по наведению
курсора begin="svg1.mouseover"
.container {
width:25%;
height:25%;
}
Анимация градиента в зоне действия треугольного значка плюс буквы
Анимация в этом примере и в других, которые ниже начинается после клика на кнопке
begin="svg1.click" при желании можно сделать по наведению курсора begin="svg1.mouseover"
.container {
width:50%;
height:50%;
}
Изменяем цветовую гамму, - чёрно-белый вариант, для этого меняем значения в атрибутах
градиента stop-color
.container {
width:45%;
height:45%;
}
Чёрно-желтый вариант
.container {
width:45%;
height:45%;
}
Диагональный градиент
Для этого добавляем в определение градиента:
Анимация только значка
.container {
width:100%;
height:100%;
}
Анимация значка и букв
.container {
width:100%;
height:100%;
}
Как с помощью scanf выделить память и занести в переменную.
Предположим, что пользователь вводит очень длинный текст и заранее определенный размер
массива обрезает строку. Пробовал не указывать размер массива и присваивать пустую
строку (""), но strlen всегда возвращает 0, а если присутствуют символы в str, то длину
строки.
Где проблема?
char str[] = "";
scanf("%s",str);
printf ("Line size \"%s\" - %d characters\n", str, strlen (str) );
Ответы
Ответ 1
Объявление
char str[] = "";
эквивалентно объявлению
char str[1] = "";
Массив получает размер 1, который после этого уже больше никогда не поменяется. В
такой массив невозможно поместить никакую строку, кроме пустой.
В любом случае, локальный или статический массив в языке С получает конкретный фиксированный
размер в точке определения. Поменять этот размер позже уже невозможно. Поэтому решить
задачу чтения строки неизвестной длины с использованием явного объявления массива не
получится.
Размер массива придется менять по мере необходимости, т.е. "на лету". А изменяться
"на лету" может только размер динамически выделенного (через malloc) массива. Однако
даже в этом случае какого-либо готового решения в стандартном scanf нет. Для чтения
строк неизвестной, заранее неограниченной длины лучше использовать не scanf, а циклический
вызов fgets с периодическим realloc-ом динамического буфера, пока не прочитана вся строка.
Тем не менее, GNU scanf поддерживает нестандартный модификатор m, реализующий требуемую
функциональность. Этот модификатор работает с форматами s и []:
char *str = NULL;
scanf("%ms", &str);
...
free(str);
Обратите внимание, что передавать в scanf в этом случае следует указатель на указатель,
т.е. аргумент типа char **.
Ранее в этой роли выступал модификатор a. Однако начиная с C99 символ a "занят" под
один из стандартных форматов. Поэтому если вы захотите воспользоваться этой возможностью,
будьте внимательны с версией библиотеки.
Я вот вижу (по своему реестру), что все деинсталяторы лежат в папках, которые они
удаляют. Но если попробовать сделать thisDir.Delete(true /*recursively*/), то вылетит
exception Access to the path 'uninstaller.exe' is denied. Ведь нельзя удалить файл,
который выполняется. Есть вариант, что uninstaller может создать исполняемый файл (tmpUninstaller.exe)
в какой-то временной директории, запустить его, tmpUninstaller.exe ждёт когда завершится
оригинальный uninstaller, зачищает всё и самоудаляется. Но, как по мне, это костыльный
вариант решения поставленной задачи. Как же всё-таки uninstaller удаляет директорию
в которой он находится?
Ответы
Ответ 1
Есть вариант, что uninstaller может создать исполняемый файл (tmpUninstaller.exe)
в какой-то временной директории, запустить его, tmpUninstaller.exe ждёт когда завершится
оригинальный uninstaller, зачищает всё и самоудаляется.
Правильно мыслите
Вот полная строка события из Process Monitor:
8:26:29,1260988 unins000.exe 20396 Process Create C:\Users\8CE3D~1\AppData\Local\Temp\_iu14D2N.tmp
SUCCESS PID: 9672, Command line: "C:\Users\8CE3D~1\AppData\Local\Temp\_iu14D2N.tmp"
/SECONDPHASE="C:\Program Files (x86)\SHTRIH-M\DrvFR 4.13\unins000.exe" /FIRSTPHASEWND=$3B1228
/INITPROCWND=$680CC6
Есть и другие способы создания самоудаляющейся программы: Самоудаление программы
Но, как по мне, это костыльный вариант решения поставленной задачи. Как же всё-таки
uninstaller удаляет директорию в которой он находится?
Инсталлятор программы в примере выше сделан с помощью Inno Setup, которая не использует
модель Windows Installer. Для инсталляторов, основанных на модели Windows Installer,
необходимости в деинсталляторе как таковом вообще нет. При установке такой программы
Windows Installer сохраняет ее MSI-пакет в каталоге c:\windows\installer. Для удаления
программы достаточно вызвать msiexec с нужными параметрами, он загрузит сохраненный
MSI, выполнит удаление, а затем удалит MSI.
Ответ 2
В дополнение к ответу от @MSDN.WhiteKnight стоит упомянуть полезную функцию WinAPI
MoveFileEx с флагом MOVEFILE_DELAY_UNTIL_REBOOT. В принципе, одной этой функции достаточно
для удаления, и некоторые инсталляторы ее и используют для самоудаления анинсталлера.
Проблема с ней только в том, что каталог программы при этом не удаляется (что мы и
видим у некоторых приложений).
Для полного удаления вместе с каталогом используется двухступенчатый процесс, как
описано в предыдущем ответе:
Записать в Temp программу (или батник) удаления.
Запустить ее, при этом удалялка должна уметь дожидаться выхода
запустившего ее анисталлера.
Вызвать MoveFileEx(szDeleterPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); для удаления
после рестарта системы удалялки из Temp.
Выйти.
Вместо вызова MoveFileEx можно удалять через реестр. Для этого в HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce
нужно создать текстовое значение с произвольным именем (например, DeleteMyApp), и строкой
cmd /C del "путь_к_программе_удаления". При следующем рестарте Windows выполнит эту
команду и удалит это значение из реестра.
Иногда нужно иметь прогрессбар с текстом внутри, а нейтивный прогрессбар не имеет
возможности выдавать текст внутри даного компонента.
Как реализовать?
В интернете всюду решения которые или с рамкой вокруг текста (тупо лейбл на прогрессбар
налеплен) или кастомные компоненты, которые банально мигают во время работы.
Ответы
Ответ 1
Т.к. всюду встречал только мигающие (blinking/flickering) решения, то решился написать
свой собственный компонент -- TextProgressBar и поделится с общественностью.
Соурс код можно найти здесь: https://github.com/ukushu/TextProgressBar
Примеры:
Особенности кода, которые нужно иметь ввиду тому, кто решится повторить "подвиг":
Отсутствие мигания кастомного компонента можно достигнуть при помощи кода, оторый
отключает "лишние" стили оригинального компонента
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer,
true);
на стадии инициализации компонента.
Так же можно столкнутся с проблемами обновления настроек заданных через VisualStudio.
Для того что бы это обойти, нужно вызывать при изменении проблемных property:
Invalidate();
что бы вижуалка перерисовала заново компонент.
И так же не нужно допускать ошибку которую я сначала допустил при написании: не забываем
что многие из классов для отрисовки элементов (да и сам контрол) являются дочерними
от IDispousable, а, значит, должны вызывать .Dispose как только будут не нужны.
Для распихивания пропертей по категориям в вижуалке нужно подписывать их следующим
образом
[Category("Additional Options")]
//обьявление самой проперти
Все остальное - просто логика отрисовки и реализации вашего кастомного компонента.
Ответ 2
Так уж вышло что заодно и WPF начал осваивать...
Мало ли, может кому пригодится:
Value/Maximum
Результат:
То же самое, но с процентом прогресса:
Раньше я думал что динамическая память нужна для того чтобы выделять там память под
массивы типов, размер которых не известен во время компиляции. Сейчас я узнал, что
в стандарте С++ нет VLA (хотя с настройками компилятора clang, VLA можно осуществить),
зато в Си VLA есть. Так зачем вообще тогда нужна динамическая память, если можно включить
VLA. Единственное мое предположение, это то что размер стека ограничен много меньшим
числом, нежели размер кучи, поэтому большой размер памяти аллоцируем в куче.
Ответы
Ответ 1
Динамическая память нужна в первую очередь для того, чтобы дать пользователю ручной
контроль над временем жизни/продолжительностью хранения создаваемых объектов. То есть
выражаясь попросту для того, чтобы можно было выделить память внутри блока (функции),
выйти из этого блока (функции), а память при этом оставалась выделенной.
Это главное назначение динамической памяти - реализовывать динамическую продолжительность
хранения, т.е. существовать "вечно": пока память не будет освобождена явной командой
освобождения памяти. Такой ручной контроль над временем жизни может использоваться
как для продления этого времени, так и для его сокращения.
Никакие VLA вам в этом не помогут. VLA - локальные автоматические объекты, время
жизни которых подчиняется правилам для локальных автоматических объектов.
Именно это свойство динамической памяти позволяет вам, например, создавать списки
или деревья с неизвестным на стадии компиляции количеством узлов, т.е. генерировать
объекты-узлы в необходимом количестве во время выполнения программы.
Вторым по значимости назначением динамической памяти уже является создание объектов,
чей тип известен только во время выполнения: полиморфные объекты или массивы заранее
не известного размера.
И, наконец, в качестве третьего, побочного, назначения динамической памяти можно
назвать создание объектов, чье время жизни является вполне автоматическим, но которые
при этом слишком велики для автоматической памяти (для размещения в стеке).
Товарищи, подскажите! Куда копать, возможно ли это вообще - как реализовать такой
круг в html, чтобы при клике на конкретное меню - оно вращалось и выбранное меню становилось
на верх (как колесо фортуны, но не рандом).
Интересует так же, как это изобразить в коде? Достаточно только css или нужно что-то
еще? Есть какие то примеры, наводки?
Update:
Заново прошу помощи! Есть вот такой круг (спасибо MaximLensky): codepen.io/topicstarter/pen/dExqLr
Как сделать из него семиугольник (здесь пятиугольник). А так же добавить сверху такие
меню, как на картинке?
Как его анимировать, чтобы при нажатии на элемент - круг вращался и наверх по центру
становился выбранный элемент, изменялся цвет? Дополнительные меню сверху - просто ссылки,
такого же цвета, как и основной элемент.
Важно, чтобы текст и цвета можно было изменить.
Так же прикрепляю примерно что-то похожее (вращение): codepen.io/AndersFly666/pen/GZxVYO
Ответы
Ответ 1
Что касается верстки всяких разных кругов, тут ваши лучшие друзья это
Math.sin Math.cos и Math.PI
Вот пример с использованием d3.js, но могло быть и без него
PS: длинный текст надо тоже вписать в круг и его вращать
Самая актуальная версия тут https://codepen.io/strangerintheq/pen/wbVRGL
let data = Array(5).fill(0).map(object)
let a = Math.PI*2/data.length, start = -Math.PI/2
let r1 = 30, r2 = 35, r3 = 37, r4 = 95;
let basePts = [];
basePts.push(pt(0, r1))
basePts.push(pt(a, r2))
basePts.push(pt(a*2-0.15, r3))
basePts.push(pt(a*1.5-0.1, r4))
let arc = `A ${r4} ${r4} 0 0 1 ${pt(a/2, r4)}` ;
append('path', 'line')
.attr('stroke', d => d.color)
.attr('fill', 'none')
.attr('d', 'M' + basePts.join('L') + arc)
.attr('transform', (d, i) => `rotate(${-i*360/data.length})`)
append('text', 'icon')
.attr('fill', d => d.color)
.html(d => d.icon)
.attr('x', (e, i) => pt(a*i+a*0.7,80)[0])
.attr('y', (e, i) => pt(a*i+a*0.7,80)[1])
append('text', 'number')
.attr('fill', d => d.color)
.html((d, i) => i+1)
.attr('x', (e, i) => pt(a*i+a*1.5,44)[0])
.attr('y', (e, i) => pt(a*i+a*1.5,44)[1])
basePts.shift();
append('path', 'active')
.attr('d', 'M' + basePts.join('L') + arc)
.attr('transform', (d, i) => `rotate(${-i*360/data.length})`)
.on('click', (d,i) => {
let angle = (i + 1)*360 / data.length
d3.select('g').attr('transform', `rotate(${angle})`)
d3.selectAll('text').attr('transform', function(){
let x = d3.select(this).attr('x')
let y = d3.select(this).attr('y')
return `rotate(${-angle},${x},${y})`;
})
})
function append(node, clazz) {
return d3.select('g')
.selectAll(node + '.' + clazz)
.data(data)
.enter()
.append(node)
.classed(clazz, true)
}
function pt (a, r) {
a = - a - Math.PI/2
return [Math.cos(a)*r, Math.sin(a)*r];
}
function object(){
return {
color: `hsl(${Math.random()*360},55%,55%)`,
icon: `ð${Math.floor(Math.random()*100)};`
}
}
text {
stroke: transparent;
dominant-baseline: middle;
text-anchor: middle;
}
.icon {
font-family: FontAwesome;
font-size: 20px;
transition: 1s;
}
.number {
font-family: arial, sans-serif;
font-size: 15px;
transition: 1s;
}
path.active{
fill: transparent;
cursor: pointer;
}
g {
transition: 1s;
}
UPD:
Классика жанра, в макете 5 углов, но надо 7, как хорошо что я не умею делать это
в inkscape, я просто поменяю пару цифр в своем коде
let data = Array(7).fill(0).map(object)
let a = Math.PI*2/data.length, start = -Math.PI/2
let r1=30, r2=35, r3=37, r4=95, rotation=0;
let pts = [];
pts.push(pt(0, r1))
pts.push(pt(a, r2))
pts.push(pt(a*2-0.15, r3))
pts.push(pt(a*1.5-0.45, r4))
let arc = (radius, angle) => `A ${radius} ${radius} 0 0 1 ${pt(angle, radius)}` ;
append('path', 'line')
.attr('stroke', d => d.color)
.attr('d', 'M' + pts.join('L') + arc(r4, a-0.7))
.attr('transform', (d, i) => `rotate(${-i*360/data.length})`)
append('text', 'icon rotate')
.attr('fill', d => d.color)
.html(d => d.icon)
.attr('x', (e, i) => pt(a*i+a*0.35,80)[0])
.attr('y', (e, i) => pt(a*i+a*0.35,80)[1])
append('path', 'text-wrap-path')
.attr('id', (d, i) => `text-wrap-path_${i}`)
.attr('x', (e, i) => pt(a*i+a*0.77,67)[0])
.attr('y', (e, i) => pt(a*i+a*0.77,67)[1])
.attr('d', (e, i) => {
let c = pt(a*i+a*0.77,67);
return Array(5).fill(0).map((e,i) => line(Math.acos(1-2/5*i)))
function line(a) {
let p1 = pt(a, 21), p2 = pt(-a, 21);
return 'M' + (c[0]+p1[0]) + ',' + (c[1]+p1[1]) +
'L' + (c[0]+p2[0]) + ',' + (c[1]+p2[1])
}
})
append('text', 'text')
.append('textPath')
.attr('href', (e,i) => `#text-wrap-path_${i}`)
.text(d => d.text)
.attr('fill', d => d.color)
.attr('startOffset', 40)
append('text', 'number rotate')
.attr('fill', d => d.color)
.html((d, i) => i + 1)
.attr('x', (e, i) => pt(a*i+a*1.2,44)[0])
.attr('y', (e, i) => pt(a*i+a*1.2,44)[1])
pts.shift();
append('path', 'active')
.attr('fill', 'transparent')
.attr('d', 'M' + pts.join('L') + arc(r4, a-0.7))
.attr('transform', (d, i) => `rotate(${-i*360/data.length})`)
.on('click', click)
lineAndText(r4+15, 0)
lineAndText(r4+30, 1)
lineAndText(r4+45, 2)
function lineAndText(r,n){
append('path', 'line1 l' + n)
.attr('id', (d, i) => `line1_l${n}_${i}`)
.attr('stroke', d => d.color)
.attr('d', 'M' + pt(a-0.05*(n+1), r) + arc(r, 0.2-0.05*(n+1)))
.attr('transform', (d, i) => `rotate(${-i*360/data.length})`)
append('text', 'menu l' + n)
.append('textPath')
.attr('href', (e,i) => `#line1_l${n}_${i}`)
.text(d => d.menu[n])
.attr('fill', d => d.color)
.attr('startOffset', 40)
}
function click(d,i) {
let angle = (i+1)*360/data.length-30
let from = rotation;
rotation = angle;
d3.selectAll('.line1').style('opacity', 0)
d3.selectAll('.line1.index_'+i).style('opacity', 1)
d3.selectAll('.menu').style('opacity', 0)
d3.selectAll('.menu.index_'+i).style('opacity', 1)
d3.select('g').transition().duration(1000)
.attrTween('transform', () => t => `rotate(${from+(angle-from)*t})`)
d3.selectAll('.text-wrap-path').transition().duration(1000)
.attrTween('transform', function() {
let x = d3.select(this).attr('x')
let y = d3.select(this).attr('y')
return t => `rotate(-${from+(angle-from)*t}, ${x}, ${y})`
})
d3.selectAll('text.rotate').transition().duration(1000)
.attrTween('transform', function() {
let x = d3.select(this).attr('x')
let y = d3.select(this).attr('y')
return t => `rotate(-${from+(angle-from)*t}, ${x}, ${y})`
})
}
function append(node, clazz) {
return d3.select('g')
.selectAll(node + '.' + clazz)
.data(data)
.enter()
.append(node)
.attr('fill', 'none')
.attr('class', (d, i) => `${clazz} index_${i}`)
}
function pt (a, r) {
a = - a - Math.PI/2
return [Math.cos(a)*r, Math.sin(a)*r];
}
function object(){
return {
color: `hsl(${Math.random()*360},55%,55%)`,
icon: `ð${Math.floor(Math.random()*100)};`,
menu: [`menu 1`, `menu 2`, `menu 3`],
text: 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur'.split(' ').filter(() => Math.random()
> 0.5).join(' ')
}
}
text {
stroke: transparent;
dominant-baseline: middle;
text-anchor: middle;
}
.icon {
font-family: FontAwesome;
font-size: 18px;
}
.number {
font-family: arial, sans-serif;
font-size: 15px;
}
path.active{
cursor: pointer;
}
.line1, .menu {
opacity: 0;
transition: 1s;
}
text.menu{
dominant-baseline: text-before-edge;
font-size:13px;
cursor:pointer;
}
textPath {
transition: 500ms;
}
text.menu:hover textPath {
fill: red;
}
text.text {
font-size: 8px;
}
Ответ 2
Накидал макет на Svg в редакторе векторной графики Inkscape
Для этого я вырезал в Gimp нужный фрагмент, и загрузил в inkscape, преимущественно
пользовался инструментом path без закрутия пути
Символы добавил с font-awesome что потребовало подключения самого шрифта с cdn и
обычное копирование и вставка самой иконки